You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2010/09/26 13:59:01 UTC

svn commit: r1001417 [2/7] - in /subversion/branches/performance: ./ build/ notes/ notes/http-and-webdav/ subversion/bindings/javahl/native/ subversion/bindings/javahl/tests/org/apache/subversion/javahl/ subversion/bindings/javahl/tests/org/tigris/subv...

Modified: subversion/branches/performance/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_serf/commit.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_serf/commit.c Sun Sep 26 11:58:58 2010
@@ -109,6 +109,10 @@ typedef struct {
   apr_hash_t *changed_props;
   apr_hash_t *removed_props;
 
+  /* Same, for the old value (*old_value_p). */
+  apr_hash_t *previous_changed_props;
+  apr_hash_t *previous_removed_props;
+
   /* In HTTP v2, this is the file/directory version we think we're changing. */
   svn_revnum_t base_revision;
 
@@ -639,16 +643,17 @@ checkout_file(file_context_t *file)
 /* Helper function for proppatch_walker() below. */
 static svn_error_t *
 get_encoding_and_cdata(const char **encoding_p,
-                       serf_bucket_t **cdata_bkt_p,
+                       const svn_string_t **encoded_value_p,
                        serf_bucket_alloc_t *alloc,
                        const svn_string_t *value,
                        apr_pool_t *pool)
 {
-  const char *encoding;
-  const char *cdata;
-  apr_size_t len; /* of cdata */
-
-  SVN_ERR_ASSERT(value);
+  if (value == NULL)
+    {
+      *encoding_p = NULL;
+      *encoded_value_p = NULL;
+      return SVN_NO_ERROR;
+    }
 
   /* If a property is XML-safe, XML-encode it.  Else, base64-encode
      it. */
@@ -656,23 +661,72 @@ get_encoding_and_cdata(const char **enco
     {
       svn_stringbuf_t *xml_esc = NULL;
       svn_xml_escape_cdata_string(&xml_esc, value, pool);
-      encoding = NULL;
-      cdata = xml_esc->data;
-      len = xml_esc->len;
+      *encoding_p = NULL;
+      *encoded_value_p = svn_string_create_from_buf(xml_esc, pool);
     }
   else
     {
-      const svn_string_t *base64ed = svn_base64_encode_string2(value, TRUE,
-                                                               pool);
-      encoding = "base64";
-      cdata = base64ed->data;
-      len = base64ed->len;
+      *encoding_p = "base64";
+      *encoded_value_p = svn_base64_encode_string2(value, TRUE, pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+typedef struct walker_baton_t {
+  serf_bucket_t *body_bkt;
+  apr_hash_t *previous_changed_props;
+  apr_hash_t *previous_removed_props;
+  const char *path;
+
+  /* Hack, since change_rev_prop(old_value_p != NULL, value = NULL) uses D:set
+     rather than D:remove...  (see notes/http-and-webdav/webdav-protocol) */
+  enum {
+    filter_all_props,
+    filter_props_with_old_value,
+    filter_props_without_old_value
+  } filter;
+
+  /* Is the property being deleted? */
+  svn_boolean_t deleting;
+} walker_baton_t;
+
+static svn_error_t *
+derive_old_val(const svn_string_t *const *   *old_val_p_p,
+               walker_baton_t *wb,
+               const char *ns,
+               const char *name,
+               apr_pool_t *pool)
+{
+  const svn_string_t *const *old_val_p;
+
+  old_val_p = NULL;
+
+  if (wb->previous_changed_props)
+    {
+      const svn_string_t *old_val;
+      old_val = svn_ra_serf__get_prop_string(wb->previous_changed_props,
+                                             wb->path, ns, name);
+      if (old_val)
+        {
+          old_val_p = apr_pcalloc(pool, sizeof(*old_val_p));
+          *(const svn_string_t **)old_val_p = svn_string_dup(old_val, pool);
+        }
     }
 
-  /* ENCODING, CDATA, and LEN are now set. */
+  if (wb->previous_removed_props)
+    {
+      const svn_string_t *old_val;
+      old_val = svn_ra_serf__get_prop_string(wb->previous_removed_props,
+                                             wb->path, ns, name);
+      if (old_val)
+        {
+          old_val_p = apr_pcalloc(pool, sizeof(*old_val_p));
+          *(const svn_string_t **)old_val_p = NULL;
+        }
+    }
 
-  *encoding_p = encoding;
-  *cdata_bkt_p = SERF_BUCKET_SIMPLE_STRING_LEN(cdata, len, alloc);
+  *old_val_p_p = old_val_p;
   return SVN_NO_ERROR;
 }
 
@@ -683,10 +737,13 @@ proppatch_walker(void *baton,
                  const svn_string_t *val,
                  apr_pool_t *pool)
 {
-  serf_bucket_t *body_bkt = baton;
+  walker_baton_t *wb = baton;
+  serf_bucket_t *body_bkt = wb->body_bkt;
   serf_bucket_t *cdata_bkt;
   serf_bucket_alloc_t *alloc;
   const char *encoding;
+  const svn_string_t *const *old_val_p;
+  const svn_string_t *encoded_value;
   char *prop_name;
 
   /* Use the namespace prefix instead of adding the xmlns attribute to support
@@ -697,14 +754,82 @@ proppatch_walker(void *baton,
     prop_name = apr_pstrcat(pool, "C:", name, NULL);
   name_len = strlen(prop_name);
 
+  SVN_ERR(derive_old_val(&old_val_p, wb, ns, name, pool));
+
+  /* Jump through hoops to work with D:remove and its val = (""-for-NULL)
+   * representation. */
+  if (wb->filter != filter_all_props)
+    {
+      if (wb->filter == filter_props_with_old_value && ! old_val_p)
+      	return SVN_NO_ERROR;
+      if (wb->filter == filter_props_without_old_value && old_val_p)
+      	return SVN_NO_ERROR;
+    }
+  if (wb->deleting)
+    val = NULL;
+
   alloc = body_bkt->allocator;
 
-  SVN_ERR(get_encoding_and_cdata(&encoding, &cdata_bkt, alloc, val, pool));
+  SVN_ERR(get_encoding_and_cdata(&encoding, &encoded_value, alloc, val, pool));
+  if (encoded_value)
+    {
+      cdata_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(encoded_value->data,
+                                                encoded_value->len,
+                                                alloc);
+    }
+  else
+    {
+      cdata_bkt = NULL;
+    }
 
-  svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name,
-                                    "V:encoding", encoding,
-                                    NULL);
-  serf_bucket_aggregate_append(body_bkt, cdata_bkt);
+  if (cdata_bkt)
+    svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name,
+                                      "V:encoding", encoding,
+                                      NULL);
+  else
+    svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, prop_name,
+                                      "V:" SVN_DAV__OLD_VALUE__ABSENT, "1",
+                                      NULL);
+
+  if (old_val_p)
+    {
+      const char *encoding2;
+      const svn_string_t *encoded_value2;
+      serf_bucket_t *cdata_bkt2;
+
+      SVN_ERR(get_encoding_and_cdata(&encoding2, &encoded_value2,
+                                     alloc, *old_val_p, pool));
+
+      if (encoded_value2)
+        {
+          cdata_bkt2 = SERF_BUCKET_SIMPLE_STRING_LEN(encoded_value2->data,
+                                                     encoded_value2->len,
+                                                     alloc);
+        }
+      else
+        {
+          cdata_bkt2 = NULL;
+        }
+
+      if (cdata_bkt2)
+        svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
+                                          "V:" SVN_DAV__OLD_VALUE,
+                                          "V:encoding", encoding2,
+                                          NULL);
+      else
+        svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
+                                          "V:" SVN_DAV__OLD_VALUE,
+                                          "V:" SVN_DAV__OLD_VALUE__ABSENT, "1",
+                                          NULL);
+
+      if (cdata_bkt2)
+        serf_bucket_aggregate_append(body_bkt, cdata_bkt2);
+
+      svn_ra_serf__add_close_tag_buckets(body_bkt, alloc,
+                                         "V:" SVN_DAV__OLD_VALUE);
+    }
+  if (cdata_bkt)
+    serf_bucket_aggregate_append(body_bkt, cdata_bkt);
   svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, prop_name);
 
   return SVN_NO_ERROR;
@@ -753,6 +878,7 @@ create_proppatch_body(serf_bucket_t **bk
 {
   proppatch_context_t *ctx = baton;
   serf_bucket_t *body_bkt;
+  walker_baton_t *wb;
 
   body_bkt = serf_bucket_aggregate_create(alloc);
 
@@ -764,14 +890,37 @@ create_proppatch_body(serf_bucket_t **bk
                                     "xmlns:S", SVN_DAV_PROP_NS_SVN,
                                     NULL);
 
+  wb = apr_pcalloc(pool, sizeof(*wb));
+  wb->body_bkt = body_bkt;
+  wb->previous_changed_props = ctx->previous_changed_props;
+  wb->previous_removed_props = ctx->previous_removed_props;
+  wb->path = ctx->path;
+
   if (apr_hash_count(ctx->changed_props) > 0)
     {
       svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:set", NULL);
       svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
 
+      wb->filter = filter_all_props;
+      wb->deleting = FALSE;
       SVN_ERR(svn_ra_serf__walk_all_props(ctx->changed_props, ctx->path,
                                           SVN_INVALID_REVNUM,
-                                          proppatch_walker, body_bkt, pool));
+                                          proppatch_walker, wb, pool));
+
+      svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
+      svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:set");
+    }
+
+  if (apr_hash_count(ctx->removed_props) > 0)
+    {
+      svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:set", NULL);
+      svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
+
+      wb->filter = filter_props_with_old_value;
+      wb->deleting = TRUE;
+      SVN_ERR(svn_ra_serf__walk_all_props(ctx->removed_props, ctx->path,
+                                          SVN_INVALID_REVNUM,
+                                          proppatch_walker, wb, pool));
 
       svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
       svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:set");
@@ -782,9 +931,11 @@ create_proppatch_body(serf_bucket_t **bk
       svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:remove", NULL);
       svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL);
 
+      wb->filter = filter_props_without_old_value;
+      wb->deleting = TRUE;
       SVN_ERR(svn_ra_serf__walk_all_props(ctx->removed_props, ctx->path,
                                           SVN_INVALID_REVNUM,
-                                          proppatch_walker, body_bkt, pool));
+                                          proppatch_walker, wb, pool));
 
       svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop");
       svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:remove");
@@ -2208,6 +2359,7 @@ svn_error_t *
 svn_ra_serf__change_rev_prop(svn_ra_session_t *ra_session,
                              svn_revnum_t rev,
                              const char *name,
+                             const svn_string_t *const *old_value_p,
                              const svn_string_t *value,
                              apr_pool_t *pool)
 {
@@ -2219,6 +2371,17 @@ svn_ra_serf__change_rev_prop(svn_ra_sess
   apr_hash_t *props;
   svn_error_t *err;
 
+  if (old_value_p)
+    {
+      svn_boolean_t capable;
+      SVN_ERR(svn_ra_serf__has_capability(ra_session, &capable,
+                                          SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
+                                          pool));
+
+      /* How did you get past the same check in svn_ra_change_rev_prop2()? */
+      SVN_ERR_ASSERT(capable);
+    }
+
   commit = apr_pcalloc(pool, sizeof(*commit));
 
   commit->pool = pool;
@@ -2265,8 +2428,28 @@ svn_ra_serf__change_rev_prop(svn_ra_sess
   proppatch_ctx->path = proppatch_target;
   proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
   proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
+  if (old_value_p)
+    {
+      proppatch_ctx->previous_changed_props = apr_hash_make(proppatch_ctx->pool);
+      proppatch_ctx->previous_removed_props = apr_hash_make(proppatch_ctx->pool);
+    }
   proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
 
+  if (old_value_p && *old_value_p)
+    {
+      svn_ra_serf__set_prop(proppatch_ctx->previous_changed_props,
+                            proppatch_ctx->path,
+                            ns, name, *old_value_p, proppatch_ctx->pool);
+    }
+  else if (old_value_p)
+    {
+      svn_string_t *dummy_value = svn_string_create("", proppatch_ctx->pool);
+
+      svn_ra_serf__set_prop(proppatch_ctx->previous_removed_props,
+                            proppatch_ctx->path,
+                            ns, name, dummy_value, proppatch_ctx->pool);
+    }
+
   if (value)
     {
       svn_ra_serf__set_prop(proppatch_ctx->changed_props, proppatch_ctx->path,

Modified: subversion/branches/performance/subversion/libsvn_ra_serf/options.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_serf/options.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_serf/options.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_serf/options.c Sun Sep 26 11:58:58 2010
@@ -313,6 +313,13 @@ capabilities_headers_iterator_callback(v
                        SVN_RA_CAPABILITY_LOG_REVPROPS,
                        APR_HASH_KEY_STRING, capability_yes);
         }
+      if (svn_cstring_match_glob_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS,
+                                      vals))
+        {
+          apr_hash_set(orc->session->capabilities,
+                       SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
+                       APR_HASH_KEY_STRING, capability_yes);
+        }
       if (svn_cstring_match_glob_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals))
         {
           apr_hash_set(orc->session->capabilities,
@@ -404,6 +411,8 @@ options_response_handler(serf_request_t 
                APR_HASH_KEY_STRING, capability_no);
   apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
                APR_HASH_KEY_STRING, capability_no);
+  apr_hash_set(orc->session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
+               APR_HASH_KEY_STRING, capability_no);
 
   /* Then see which ones we can discover. */
   serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback, orc);

Modified: subversion/branches/performance/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_serf/ra_serf.h?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/branches/performance/subversion/libsvn_ra_serf/ra_serf.h Sun Sep 26 11:58:58 2010
@@ -673,6 +673,9 @@ typedef struct svn_ra_serf__server_error
   /* Have we seen an error tag? */
   svn_boolean_t in_error;
 
+  /* Have we seen a HTTP "412 Precondition Failed" error? */
+  svn_boolean_t contains_precondition_error;
+
   /* Should we be collecting the XML cdata? */
   svn_boolean_t collect_cdata;
 
@@ -1322,6 +1325,7 @@ svn_error_t *
 svn_ra_serf__change_rev_prop(svn_ra_session_t *session,
                              svn_revnum_t rev,
                              const char *name,
+                             const svn_string_t *const *old_value_p,
                              const svn_string_t *value,
                              apr_pool_t *pool);
 

Modified: subversion/branches/performance/subversion/libsvn_ra_serf/replay.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_serf/replay.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_serf/replay.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_serf/replay.c Sun Sep 26 11:58:58 2010
@@ -90,7 +90,7 @@ typedef struct {
   replay_info_t *parent;
 } prop_info_t;
 
-typedef struct {
+typedef struct replay_context_t {
   apr_pool_t *src_rev_pool;
   apr_pool_t *dst_rev_pool;
 
@@ -118,6 +118,10 @@ typedef struct {
   /* Cached report target url */
   const char *report_target;
 
+  /* Target and revision to fetch revision properties on */
+  const char *revprop_target;
+  svn_revnum_t revprop_rev;
+
   /* Revision properties for this revision. */
   apr_hash_t *revs_props;
   apr_hash_t *props;
@@ -125,6 +129,9 @@ typedef struct {
   /* Keep a reference to the XML parser ctx to report any errors. */
   svn_ra_serf__xml_parser_t *parser_ctx;
 
+  /* The propfind for the revision properties of the current revision */
+  svn_ra_serf__propfind_context_t *prop_ctx;
+
 } replay_context_t;
 
 
@@ -180,11 +187,15 @@ start_replay(svn_ra_serf__xml_parser_t *
     {
       push_state(parser, ctx, REPORT);
 
+      /* Before we can continue, we need the revision properties. */
+      SVN_ERR_ASSERT(!ctx->prop_ctx
+                     || svn_ra_serf__propfind_is_done(ctx->prop_ctx));
+
       /* Create a pool for the commit editor. */
       ctx->dst_rev_pool = svn_pool_create(ctx->src_rev_pool);
       ctx->props = apr_hash_make(ctx->dst_rev_pool);
-      SVN_ERR(svn_ra_serf__walk_all_props(ctx->revs_props, ctx->report_target,
-                                          ctx->revision,
+      SVN_ERR(svn_ra_serf__walk_all_props(ctx->revs_props, ctx->revprop_target,
+                                          ctx->revprop_rev,
                                           svn_ra_serf__set_bare_props,
                                           ctx->props, ctx->dst_rev_pool));
       if (ctx->revstart_func)
@@ -751,13 +762,30 @@ svn_ra_serf__replay_range(svn_ra_session
           /* Request all properties of a certain revision. */
           replay_ctx->report_target = report_target;
           replay_ctx->revs_props = apr_hash_make(replay_ctx->src_rev_pool);
+
+          if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
+           {
+             replay_ctx->revprop_target = apr_psprintf(pool, "%s/%ld",
+                                                       session->rev_stub, rev);
+             replay_ctx->revprop_rev = SVN_INVALID_REVNUM;
+            }
+          else
+            {
+              replay_ctx->revprop_target = report_target;
+              replay_ctx->revprop_rev = rev;
+            }
+
           SVN_ERR(svn_ra_serf__deliver_props(&prop_ctx,
                                              replay_ctx->revs_props, session,
-                                             session->conns[0], report_target,
-                                             rev,  "0", all_props,
+                                             session->conns[0],
+                                             replay_ctx->revprop_target,
+                                             replay_ctx->revprop_rev,
+                                             "0", all_props,
                                              TRUE, NULL,
                                              replay_ctx->src_rev_pool));
 
+          replay_ctx->prop_ctx = prop_ctx;
+
           /* Send the replay report request. */
           handler = apr_pcalloc(replay_ctx->src_rev_pool, sizeof(*handler));
 

Modified: subversion/branches/performance/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_serf/util.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_serf/util.c Sun Sep 26 11:58:58 2010
@@ -836,6 +836,7 @@ svn_ra_serf__handle_discard_body(serf_re
             {
               server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
               server_err->has_xml_response = TRUE;
+              server_err->contains_precondition_error = FALSE;
               server_err->cdata = svn_stringbuf_create("", pool);
               server_err->collect_cdata = FALSE;
               server_err->parser.pool = server_err->error->pool;
@@ -945,6 +946,34 @@ svn_ra_serf__handle_status_only(serf_req
   return svn_error_return(err);
 }
 
+/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric
+   status code into *STATUS_CODE_OUT.  Ignores leading whitespace. */
+static svn_error_t *
+parse_dav_status(int *status_code_out, svn_stringbuf_t *buf,
+                 apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+  const char *token;
+  char *tok_status;
+  svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool);
+
+  svn_stringbuf_strip_whitespace(temp_buf);
+  token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
+  if (token)
+    token = apr_strtok(NULL, " \t\r\n", &tok_status);
+  if (!token)
+    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                             "Malformed DAV:status CDATA '%s'",
+                             buf->data);
+  err = svn_cstring_atoi(status_code_out, token);
+  if (err)
+    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
+                             "Malformed DAV:status CDATA '%s'",
+                             buf->data);
+
+  return SVN_NO_ERROR;
+}
+
 /*
  * Expat callback invoked on a start element tag for a 207 response.
  */
@@ -968,6 +997,14 @@ start_207(svn_ra_serf__xml_parser_t *par
       svn_stringbuf_setempty(ctx->cdata);
       ctx->collect_cdata = TRUE;
     }
+  else if (ctx->in_error &&
+           strcmp(name.namespace, "DAV:") == 0 &&
+           strcmp(name.name, "status") == 0)
+    {
+      /* Start collecting cdata. */
+      svn_stringbuf_setempty(ctx->cdata);
+      ctx->collect_cdata = TRUE;
+    }
 
   return SVN_NO_ERROR;
 }
@@ -993,7 +1030,22 @@ end_207(svn_ra_serf__xml_parser_t *parse
       ctx->collect_cdata = FALSE;
       ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
                                            ctx->cdata->len);
-      ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
+      if (ctx->contains_precondition_error)
+        ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH;
+      else
+        ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
+    }
+  else if (ctx->in_error &&
+           strcmp(name.namespace, "DAV:") == 0 &&
+           strcmp(name.name, "status") == 0)
+    {
+      int status_code;
+
+      ctx->collect_cdata = FALSE;
+
+      SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool));
+      if (status_code == 412)
+        ctx->contains_precondition_error = TRUE;
     }
 
   return SVN_NO_ERROR;
@@ -1044,6 +1096,7 @@ svn_ra_serf__handle_multistatus_only(ser
         {
           server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
           server_err->has_xml_response = TRUE;
+          server_err->contains_precondition_error = FALSE;
           server_err->cdata = svn_stringbuf_create("", pool);
           server_err->collect_cdata = FALSE;
           server_err->parser.pool = server_err->error->pool;
@@ -1053,7 +1106,7 @@ svn_ra_serf__handle_multistatus_only(ser
           server_err->parser.cdata = cdata_207;
           server_err->parser.done = &ctx->done;
           server_err->parser.ignore_errors = TRUE;
-    }
+        }
       else
         {
           ctx->done = TRUE;

Modified: subversion/branches/performance/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_svn/client.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/branches/performance/subversion/libsvn_ra_svn/client.c Sun Sep 26 11:58:58 2010
@@ -622,6 +622,7 @@ static svn_error_t *open_session(svn_ra_
   /* In protocol version 2, we send back our protocol version, our
    * capability list, and the URL, and subsequently there is an auth
    * request. */
+  /* Client-side capabilities list: */
   SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(wwwwww)cc(?c)",
                                  (apr_uint64_t) 2,
                                  SVN_RA_SVN_CAP_EDIT_PIPELINE,
@@ -813,16 +814,49 @@ static svn_error_t *ra_svn_get_dated_rev
   return SVN_NO_ERROR;
 }
 
+/* Forward declaration. */
+static svn_error_t *ra_svn_has_capability(svn_ra_session_t *session,
+                                          svn_boolean_t *has,
+                                          const char *capability,
+                                          apr_pool_t *pool);
+
 static svn_error_t *ra_svn_change_rev_prop(svn_ra_session_t *session, svn_revnum_t rev,
                                            const char *name,
+                                           const svn_string_t *const *old_value_p,
                                            const svn_string_t *value,
                                            apr_pool_t *pool)
 {
   svn_ra_svn__session_baton_t *sess_baton = session->priv;
   svn_ra_svn_conn_t *conn = sess_baton->conn;
+  svn_boolean_t dont_care;
+  const svn_string_t *old_value;
+  svn_boolean_t has_atomic_revprops;
+
+  SVN_ERR(ra_svn_has_capability(session, &has_atomic_revprops,
+                                SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
+                                pool));
+
+  if (old_value_p)
+    {
+      /* How did you get past the same check in svn_ra_change_rev_prop2()? */
+      SVN_ERR_ASSERT(has_atomic_revprops);
+
+      dont_care = FALSE;
+      old_value = *old_value_p;
+    }
+  else
+    {
+      dont_care = TRUE;
+      old_value = NULL;
+    }
+
+  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));
+  else
+    SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "change-rev-prop", "rc?s",
+                                 rev, name, value));
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "change-rev-prop", "rc?s",
-                               rev, name, value));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, ""));
   return SVN_NO_ERROR;
@@ -2420,6 +2454,9 @@ static svn_error_t *ra_svn_has_capabilit
   else if (strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0)
     *has = svn_ra_svn_has_capability(sess->conn,
                                      SVN_RA_SVN_CAP_COMMIT_REVPROPS);
+  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  /* Don't know any other capabilities, so error. */
     {
       return svn_error_createf

Modified: subversion/branches/performance/subversion/libsvn_ra_svn/protocol
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_ra_svn/protocol?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_ra_svn/protocol (original)
+++ subversion/branches/performance/subversion/libsvn_ra_svn/protocol Sun Sep 26 11:58:58 2010
@@ -199,6 +199,9 @@ capability and C indicates a client capa
 [S]  depth             If the server presents this capability, it understands
                        requested operational depth (see section 3.1.1) and
                        per-path ambient depth (see section 3.1.3).
+[S]  atomic-revprops   If the server presents this capability, it
+                       supports the change-rev-prop2 command.
+                       See section 3.1.1.
 
 3. Commands
 -----------
@@ -261,6 +264,16 @@ second place for auth-request point as n
      changed to be optional without creating an optional tuple for
      that one parameter as we normally do.)
 
+  change-rev-prop2
+    params:   ( rev:number name:string [ value:string ]
+                ( dont-care:bool ? previous-value:string ) )
+    response: ( )
+    If value is not specified, the rev-prop is removed.  If dont-care is false,
+    then the rev-prop is changed only if it is currently set as previous-value
+    indicates.  (If dont-care is false and previous-value is unspecified, then
+    the revision property must be previously unset.)  If dont-care is true,
+    then previous-value must not be specified.
+
   rev-proplist
     params:   ( rev:number )
     response: ( props:proplist )

Modified: subversion/branches/performance/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_repos/deprecated.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/performance/subversion/libsvn_repos/deprecated.c Sun Sep 26 11:58:58 2010
@@ -328,6 +328,26 @@ svn_repos_replay(svn_fs_root_t *root,
 
 /*** From fs-wrap.c ***/
 svn_error_t *
+svn_repos_fs_change_rev_prop3(svn_repos_t *repos,
+                              svn_revnum_t rev,
+                              const char *author,
+                              const char *name,
+                              const svn_string_t *new_value,
+                              svn_boolean_t use_pre_revprop_change_hook,
+                              svn_boolean_t use_post_revprop_change_hook,
+                              svn_repos_authz_func_t authz_read_func,
+                              void *authz_read_baton,
+                              apr_pool_t *pool)
+{
+  return svn_repos_fs_change_rev_prop4(repos, rev, author, name, NULL,
+                                       new_value,
+                                       use_pre_revprop_change_hook,
+                                       use_post_revprop_change_hook,
+                                       authz_read_func,
+                                       authz_read_baton, pool);
+}
+
+svn_error_t *
 svn_repos_fs_change_rev_prop2(svn_repos_t *repos,
                               svn_revnum_t rev,
                               const char *author,

Modified: subversion/branches/performance/subversion/libsvn_repos/fs-wrap.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_repos/fs-wrap.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_repos/fs-wrap.c (original)
+++ subversion/branches/performance/subversion/libsvn_repos/fs-wrap.c Sun Sep 26 11:58:58 2010
@@ -263,10 +263,11 @@ svn_repos_fs_change_txn_prop(svn_fs_txn_
 
 
 svn_error_t *
-svn_repos_fs_change_rev_prop3(svn_repos_t *repos,
+svn_repos_fs_change_rev_prop4(svn_repos_t *repos,
                               svn_revnum_t rev,
                               const char *author,
                               const char *name,
+                              const svn_string_t *const *old_value_p,
                               const svn_string_t *new_value,
                               svn_boolean_t use_pre_revprop_change_hook,
                               svn_boolean_t use_post_revprop_change_hook,
@@ -274,7 +275,6 @@ svn_repos_fs_change_rev_prop3(svn_repos_
                               void *authz_read_baton,
                               apr_pool_t *pool)
 {
-  svn_string_t *old_value;
   svn_repos_revision_access_level_t readability;
   char action;
 
@@ -284,9 +284,26 @@ svn_repos_fs_change_rev_prop3(svn_repos_
 
   if (readability == svn_repos_revision_access_full)
     {
+      const svn_string_t *old_value;
+
       SVN_ERR(validate_prop(name, new_value, pool));
-      SVN_ERR(svn_fs_revision_prop(&old_value, repos->fs, rev, name, pool));
 
+      /* Fetch OLD_VALUE for svn_fs_change_rev_prop2(). */
+      if (old_value_p)
+        {
+          old_value = *old_value_p;
+        }
+      else
+        {
+          /* Get OLD_VALUE anyway, in order for ACTION and OLD_VALUE arguments
+           * to the hooks to be accurate. */
+          svn_string_t *old_value2;
+
+          SVN_ERR(svn_fs_revision_prop(&old_value2, repos->fs, rev, name, pool));
+          old_value = old_value2; 
+        }
+
+      /* Prepare ACTION. */
       if (! new_value)
         action = 'D';
       else if (! old_value)
@@ -294,12 +311,13 @@ svn_repos_fs_change_rev_prop3(svn_repos_
       else
         action = 'M';
 
+      /* ### currently not passing the old_value to hooks */
       if (use_pre_revprop_change_hook)
         SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, rev, author, name,
                                                     new_value, action, pool));
 
-      SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name, NULL, 
-                                      new_value, pool));
+      SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name,
+                                      &old_value, new_value, pool));
 
       if (use_post_revprop_change_hook)
         SVN_ERR(svn_repos__hooks_post_revprop_change(repos, rev, author,  name,

Modified: subversion/branches/performance/subversion/libsvn_subr/error.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_subr/error.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_subr/error.c (original)
+++ subversion/branches/performance/subversion/libsvn_subr/error.c Sun Sep 26 11:58:58 2010
@@ -269,6 +269,18 @@ svn_error_root_cause(svn_error_t *err)
   return err;
 }
 
+svn_boolean_t
+svn_error_has_cause(svn_error_t *err, apr_status_t apr_err)
+{
+  svn_error_t *child;
+
+  for (child = err; child; child = child->child)
+    if (child->apr_err == apr_err)
+      return TRUE;
+
+  return FALSE;
+}
+
 svn_error_t *
 svn_error_dup(svn_error_t *err)
 {

Modified: subversion/branches/performance/subversion/libsvn_subr/sqlite.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_subr/sqlite.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_subr/sqlite.c (original)
+++ subversion/branches/performance/subversion/libsvn_subr/sqlite.c Sun Sep 26 11:58:58 2010
@@ -941,6 +941,11 @@ svn_sqlite__open(svn_sqlite__db_t **db, 
   SVN_ERR(exec_sql(*db, "PRAGMA foreign_keys=ON;"));
 #endif
 
+  /* Store temporary tables in RAM instead of in temporary files, but don't
+     fail on this if this option is disabled in the sqlite compilation by
+     setting SQLITE_TEMP_STORE to 0 (always to disk) */
+  svn_error_clear(exec_sql(*db, "PRAGMA temp_store = MEMORY;"));
+
   /* Validate the schema, upgrading if necessary. */
   if (upgrade_sql != NULL)
     SVN_ERR(check_format(*db, latest_schema, upgrade_sql, scratch_pool));

Modified: subversion/branches/performance/subversion/libsvn_subr/svn_string.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_subr/svn_string.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_subr/svn_string.c (original)
+++ subversion/branches/performance/subversion/libsvn_subr/svn_string.c Sun Sep 26 11:58:58 2010
@@ -698,7 +698,7 @@ svn_cstring_strtoui64(apr_uint64_t *n, c
       val < 0 || (apr_uint64_t)val < minval || (apr_uint64_t)val > maxval)
     return svn_error_return(
              svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
-                               _("Number '%s' is out of range '[%lu, %lu]'"),
+                               _("Number '%s' is out of range '[%llu, %llu]'"),
                                str, minval, maxval));
   *n = val;
   return SVN_NO_ERROR;
@@ -742,7 +742,7 @@ svn_cstring_strtoi64(apr_int64_t *n, con
       val < minval || val > maxval)
     return svn_error_return(
              svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
-                               _("Number '%s' is out of range '[%ld, %ld]'"),
+                               _("Number '%s' is out of range '[%lld, %lld]'"),
                                str, minval, maxval));
   *n = val;
   return SVN_NO_ERROR;

Modified: subversion/branches/performance/subversion/libsvn_wc/copy.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/copy.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/copy.c (original)
+++ subversion/branches/performance/subversion/libsvn_wc/copy.c Sun Sep 26 11:58:58 2010
@@ -46,7 +46,8 @@
 
 /*** Code. ***/
 
-/* Make a copy of SRC_ABSPATH under a temporary name in the directory
+/* Make a copy of the filesystem node (or tree if RECURSIVE) at
+   SRC_ABSPATH under a temporary name in the directory
    TMPDIR_ABSPATH and return the absolute path of the copy in
    *DST_ABSPATH.  Return the node kind of SRC_ABSPATH in *KIND.  If
    SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate
@@ -88,6 +89,10 @@ copy_to_tmpdir(const char **dst_abspath,
      ### handle the directory case and b) we need to be able to remove
      ### the cleanup before queueing the move work item. */
 
+  /* Set DST_ABSPATH to a temporary unique path.  If *KIND is file, leave a
+     file there and then overwrite it; otherwise leave no node on disk at
+     that path.  In the latter case, something else might use that path
+     before we get around to using it a moment later, but never mind. */
   SVN_ERR(svn_io_open_unique_file3(NULL, dst_abspath, tmpdir_abspath,
                                    delete_when, scratch_pool, scratch_pool));
 
@@ -115,10 +120,87 @@ copy_to_tmpdir(const char **dst_abspath,
 }
 
 
-/* A replacement for both copy_file_administratively and
-   copy_added_file_administratively.  Not yet fully working.  Relies
-   on in-db-props.  SRC_ABSPATH is a versioned file but the filesystem
-   node might not be a file.
+/* If SRC_ABSPATH and DST_ABSPATH use different pristine stores, copy the
+   pristine text of SRC_ABSPATH (if there is one) into the pristine text
+   store connected to DST_ABSPATH.  This will only happen when copying into
+   a separate WC such as an external directory.
+ */
+static svn_error_t *
+copy_pristine_text_if_necessary(svn_wc__db_t *db,
+                                const char *src_abspath,
+                                const char *dst_abspath,
+                                svn_cancel_func_t cancel_func,
+                                void *cancel_baton,
+                                apr_pool_t *scratch_pool)
+{
+  const svn_checksum_t *checksum;
+
+  SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL,
+                               &checksum,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL,
+                               db, src_abspath,
+                               scratch_pool, scratch_pool));
+  if (checksum)
+    {
+      svn_boolean_t present;
+      svn_stream_t *src_pristine, *tmp_pristine;
+      const char *tmp_pristine_abspath;
+      const svn_checksum_t *sha1_checksum, *md5_checksum;
+      const char *tmpdir_abspath;
+
+      /* If it's already in DST_ABSPATH's pristine store, we're done. */
+      SVN_ERR(svn_wc__db_pristine_check(&present, db, dst_abspath, checksum,
+                                        scratch_pool));
+      if (present)
+        return SVN_NO_ERROR;
+
+      if (checksum->kind == svn_checksum_md5)
+        {
+          md5_checksum = checksum;
+          SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db,
+                                               src_abspath, checksum,
+                                               scratch_pool, scratch_pool));
+        }
+      else
+        {
+          sha1_checksum = checksum;
+          SVN_ERR(svn_wc__db_pristine_get_md5(&md5_checksum, db,
+                                              src_abspath, checksum,
+                                              scratch_pool, scratch_pool));
+        }
+
+      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db, dst_abspath,
+                                             scratch_pool, scratch_pool));
+
+      SVN_ERR(svn_wc__db_pristine_read(&src_pristine, db,
+                                       src_abspath, sha1_checksum,
+                                       scratch_pool, scratch_pool));
+      SVN_ERR(svn_stream_open_unique(&tmp_pristine, &tmp_pristine_abspath,
+                                     tmpdir_abspath, svn_io_file_del_none,
+                                     scratch_pool, scratch_pool));
+      SVN_ERR(svn_stream_copy3(src_pristine, tmp_pristine,
+                               cancel_func, cancel_baton,
+                               scratch_pool));
+      SVN_ERR(svn_wc__db_pristine_install(db, tmp_pristine_abspath,
+                                          sha1_checksum, md5_checksum,
+                                          scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Copy the versioned file SRC_ABSPATH in DB to the path DST_ABSPATH in DB.
+   If METADATA_ONLY is true, copy only the versioned metadata,
+   otherwise copy both the versioned metadata and the filesystem node (even
+   if it is the wrong kind, and recursively if it is a dir).
+
+   A replacement for both copy_file_administratively and
+   copy_added_file_administratively.
+
+   ### Not yet fully working.  Relies on in-db-props.
 
    This also works for versioned symlinks that are stored in the db as
    svn_wc__db_kind_file with svn:special set. */
@@ -136,62 +218,20 @@ copy_versioned_file(svn_wc__db_t *db,
   svn_skel_t *work_items = NULL;
   const char *dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
   const char *tmpdir_abspath;
-  svn_stream_t *src_pristine;
   const char *tmp_dst_abspath;
   svn_node_kind_t kind;
 
   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db, dst_abspath,
                                          scratch_pool, scratch_pool));
 
-  /* This goes away when we centralise, but until then we might need
-     to do a cross-db pristine copy. */
-  if (strcmp(svn_dirent_dirname(src_abspath, scratch_pool),
-             svn_dirent_dirname(dst_abspath, scratch_pool)))
-    {
-      const svn_checksum_t *checksum;
-
-      SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                   NULL, NULL, NULL, NULL,
-                                   &checksum,
-                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                   NULL, NULL, NULL, NULL, NULL,
-                                   db, src_abspath,
-                                   scratch_pool, scratch_pool));
-      if (checksum)
-        {
-          svn_stream_t *tmp_pristine;
-          const char *tmp_pristine_abspath;
-          const svn_checksum_t *sha1_checksum, *md5_checksum;
-
-          if (checksum->kind == svn_checksum_md5)
-            {
-              md5_checksum = checksum;
-              SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db,
-                                                   src_abspath, checksum,
-                                                   scratch_pool, scratch_pool));
-            }
-          else
-            {
-              sha1_checksum = checksum;
-              SVN_ERR(svn_wc__db_pristine_get_md5(&md5_checksum, db,
-                                                  src_abspath, checksum,
-                                                  scratch_pool, scratch_pool));
-            }
-          SVN_ERR(svn_wc__db_pristine_read(&src_pristine, db,
-                                           src_abspath, sha1_checksum,
-                                           scratch_pool, scratch_pool));
-          SVN_ERR(svn_stream_open_unique(&tmp_pristine, &tmp_pristine_abspath,
-                                         tmpdir_abspath, svn_io_file_del_none,
-                                         scratch_pool, scratch_pool));
-          SVN_ERR(svn_stream_copy3(src_pristine, tmp_pristine,
-                                   cancel_func, cancel_baton,
-                                   scratch_pool));
-          SVN_ERR(svn_wc__db_pristine_install(db, tmp_pristine_abspath,
-                                              sha1_checksum, md5_checksum,
-                                              scratch_pool));
-        }
-    }
+  /* In case we are copying from one WC to another (e.g. an external dir),
+     ensure the destination WC has a copy of the pristine text. */
+  SVN_ERR(copy_pristine_text_if_necessary(db, src_abspath, dst_abspath,
+                                          cancel_func, cancel_baton,
+                                          scratch_pool));
 
+  /* Prepare a temp copy of the filesystem node.  It is usually a file, but
+     copy recursively if it's a dir. */
   if (!metadata_only)
     {
       SVN_ERR(copy_to_tmpdir(&tmp_dst_abspath, &kind, src_abspath,
@@ -209,6 +249,8 @@ copy_versioned_file(svn_wc__db_t *db,
         }
     }
 
+  /* Copy the (single) node's metadata, and move the new filesystem node
+     into place. */
   SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath,
                              work_items, scratch_pool));
   SVN_ERR(svn_wc__wq_run(db, dir_abspath,
@@ -225,6 +267,10 @@ copy_versioned_file(svn_wc__db_t *db,
   return SVN_NO_ERROR;
 }
 
+/* Copy the versioned dir SRC_ABSPATH in DB to the path DST_ABSPATH in DB,
+   recursively.  If METADATA_ONLY is true, copy only the versioned metadata,
+   otherwise copy both the versioned metadata and the filesystem nodes (even
+   if they are the wrong kind, and including unversioned children). */
 static svn_error_t *
 copy_versioned_dir(svn_wc__db_t *db,
                    const char *src_abspath,
@@ -246,6 +292,7 @@ copy_versioned_dir(svn_wc__db_t *db,
   apr_pool_t *iterpool;
   int i;
 
+  /* Prepare a temp copy of the single filesystem node (usually a dir). */
   if (!metadata_only)
     {
       SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
@@ -266,6 +313,8 @@ copy_versioned_dir(svn_wc__db_t *db,
         }
     }
 
+  /* Copy the (single) node's metadata, and move the new filesystem node
+     into place. */
   SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath,
                              work_items, scratch_pool));
   SVN_ERR(svn_wc__wq_run(db, dir_abspath,
@@ -281,8 +330,8 @@ copy_versioned_dir(svn_wc__db_t *db,
     }
 
   if (!metadata_only && kind == svn_node_dir)
-    /* All children, versioned and unversioned.  We're only interested in the
-       names of the children, so we can pass TRUE as the only_check_type
+    /* All filesystem children, versioned and unversioned.  We're only
+       interested in their names, so we can pass TRUE as the only_check_type
        param. */
     SVN_ERR(svn_io_get_dirents3(&children, src_abspath, TRUE,
                                 scratch_pool, scratch_pool));
@@ -330,9 +379,9 @@ copy_versioned_dir(svn_wc__db_t *db,
         apr_hash_set(children, child_name, APR_HASH_KEY_STRING, NULL);
     }
 
+  /* Copy all the remaining filesystem children, which are unversioned. */
   if (!metadata_only && kind == svn_node_dir)
     {
-      /* All the remaining children are unversioned. */
       apr_hash_index_t *hi;
 
       for (hi = apr_hash_first(scratch_pool, children); hi;
@@ -392,7 +441,6 @@ svn_wc_copy3(svn_wc_context_t *wc_ctx,
              apr_pool_t *scratch_pool)
 {
   svn_wc__db_t *db = wc_ctx->db;
-  svn_node_kind_t src_kind;
   svn_wc__db_kind_t src_db_kind;
   const char *dstdir_abspath;
   
@@ -401,6 +449,8 @@ svn_wc_copy3(svn_wc_context_t *wc_ctx,
   
   dstdir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
 
+  /* Ensure DSTDIR_ABSPATH belongs to the same repository as SRC_ABSPATH;
+     throw an error if not. */
   {
     svn_wc__db_status_t src_status, dstdir_status;
     const char *src_repos_root_url, *dst_repos_root_url;
@@ -481,6 +531,7 @@ svn_wc_copy3(svn_wc_context_t *wc_ctx,
          SVN_ERR_WC_INVALID_SCHEDULE, NULL,
          _("Cannot copy to '%s' as it is scheduled for deletion"),
          svn_dirent_local_style(dst_abspath, scratch_pool));
+         /* ### should report dstdir_abspath instead of dst_abspath? */
   }
 
   /* TODO(#2843): Rework the error report. */
@@ -528,16 +579,13 @@ svn_wc_copy3(svn_wc_context_t *wc_ctx,
         }
   }
 
-  SVN_ERR(svn_io_check_path(src_abspath, &src_kind, scratch_pool));
-
+  /* Check that the target path is not obstructed, if required. */
   if (!metadata_only)
     {
       svn_node_kind_t dst_kind;
 
-      /* This is the error checking from copy_file_administratively
-         but converted to wc-ng.  It's not in copy_file since this
-         checking only needs to happen at the root of the copy and not
-         when called recursively. */
+      /* (We need only to check the root of the copy, not every path inside
+         copy_versioned_file/_dir.) */
       SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind, scratch_pool));
       if (dst_kind != svn_node_none)
         return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,

Modified: subversion/branches/performance/subversion/libsvn_wc/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/deprecated.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/deprecated.c (original)
+++ subversion/branches/performance/subversion/libsvn_wc/deprecated.c Sun Sep 26 11:58:58 2010
@@ -3364,12 +3364,16 @@ svn_wc_relocate3(const char *path,
   const char *local_abspath;
   svn_wc_context_t *wc_ctx;
 
+  if (! recurse)
+    svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, 0,
+                     _("Non-recursive relocation not supported"));
+
   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
   SVN_ERR(svn_wc__context_create_with_db(&wc_ctx, NULL /* config */,
                                          svn_wc__adm_get_db(adm_access),
                                          pool));
 
-  SVN_ERR(svn_wc_relocate4(wc_ctx, local_abspath, from, to, recurse,
+  SVN_ERR(svn_wc_relocate4(wc_ctx, local_abspath, from, to,
                            validator, validator_baton, pool));
 
   return svn_error_return(svn_wc_context_destroy(wc_ctx));

Modified: subversion/branches/performance/subversion/libsvn_wc/entries.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/entries.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/entries.c (original)
+++ subversion/branches/performance/subversion/libsvn_wc/entries.c Sun Sep 26 11:58:58 2010
@@ -606,16 +606,35 @@ read_one_entry(const svn_wc_entry_t **ne
         {
           svn_sqlite__db_t *sdb;
           svn_sqlite__stmt_t *stmt;
+#ifdef SVN_WC__NODES
+          svn_sqlite__stmt_t *stmt_nodes;
+          svn_boolean_t have_nodes_row;
+#endif
 
           SVN_ERR(svn_wc__db_temp_borrow_sdb(
                     &sdb, db, dir_abspath,
                     svn_wc__db_openmode_readonly,
                     scratch_pool));
 
+#ifndef SVN_WC__NODES_ONLY
           SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
                                             STMT_SELECT_NOT_PRESENT));
           SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, entry->name));
           SVN_ERR(svn_sqlite__step(&have_row, stmt));
+#endif
+#ifdef SVN_WC__NODES
+          SVN_ERR(svn_sqlite__get_statement(&stmt_nodes, sdb,
+                                            STMT_SELECT_NOT_PRESENT));
+          SVN_ERR(svn_sqlite__bindf(stmt_nodes, "is", wc_id, entry->name));
+          SVN_ERR(svn_sqlite__step(&have_nodes_row, stmt_nodes));
+#ifndef SVN_WC__NODES_ONLY
+          SVN_ERR_ASSERT(have_row == have_nodes_row);
+          SVN_ERR(svn_sqlite__reset(stmt_nodes));
+#else
+          stmt = stmt_nodes;
+          have_row = have_nodes_row;
+#endif
+#endif
           SVN_ERR(svn_sqlite__reset(stmt));
         }
 
@@ -1727,7 +1746,8 @@ insert_working_node(svn_sqlite__db_t *sd
   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isisnnnnsnrisnnni",
                             working_node->wc_id, working_node->local_relpath,
-                            (working_node->parent_relpath == NULL ? 1 : 2),
+                            (working_node->parent_relpath == NULL
+                             ? (apr_int64_t)1 : (apr_int64_t)2),
                             working_node->parent_relpath,
                             /* Setting depth for files? */
                             svn_depth_to_word(working_node->depth),
@@ -2137,6 +2157,7 @@ write_entry(svn_wc__db_t *db,
                                                   &entry->file_external_rev,
                                                   scratch_pool));
 
+#ifndef SVN_WC__NODES_ONLY
           SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
                                             STMT_UPDATE_FILE_EXTERNAL));
           SVN_ERR(svn_sqlite__bindf(stmt, "iss",
@@ -2144,6 +2165,16 @@ write_entry(svn_wc__db_t *db,
                                     entry->name,
                                     str));
           SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+#ifdef SVN_WC__NODES
+          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+                                            STMT_UPDATE_FILE_EXTERNAL_1));
+          SVN_ERR(svn_sqlite__bindf(stmt, "iss",
+                                    (apr_uint64_t)1 /* wc_id */,
+                                    entry->name,
+                                    str));
+          SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
         }
     }
 

Modified: subversion/branches/performance/subversion/libsvn_wc/relocate.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/relocate.c?rev=1001417&r1=1001416&r2=1001417&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/relocate.c (original)
+++ subversion/branches/performance/subversion/libsvn_wc/relocate.c Sun Sep 26 11:58:58 2010
@@ -76,7 +76,6 @@ svn_wc_relocate4(svn_wc_context_t *wc_ct
                  const char *local_abspath,
                  const char *from,
                  const char *to,
-                 svn_boolean_t recurse,
                  svn_wc_relocation_validator3_t validator,
                  void *validator_baton,
                  apr_pool_t *scratch_pool)
@@ -87,6 +86,35 @@ svn_wc_relocate4(svn_wc_context_t *wc_ct
   const char *old_url;
   const char *new_repos_root;
   const char *uuid;
+  svn_boolean_t is_wc_root;
+
+  SVN_ERR(svn_wc__strictly_is_wc_root(&is_wc_root, wc_ctx, local_abspath,
+                                      scratch_pool));
+  if (! is_wc_root)
+    {
+      const char *wcroot_abspath;
+      svn_error_t *err;
+
+      err = svn_wc__db_get_wcroot(&wcroot_abspath, wc_ctx->db,
+                                  local_abspath, scratch_pool, scratch_pool);
+      if (err)
+        {
+          svn_error_clear(err);
+          return svn_error_createf(
+            SVN_ERR_WC_INVALID_OP_ON_CWD, NULL,
+            _("Cannot relocate '%s' as it is not the root of a working copy"),
+            svn_dirent_local_style(local_abspath, scratch_pool));
+        }
+      else
+        {
+          return svn_error_createf(
+            SVN_ERR_WC_INVALID_OP_ON_CWD, NULL,
+            _("Cannot relocate '%s' as it is not the root of a working copy; "
+              "try relocating '%s' instead"),
+            svn_dirent_local_style(local_abspath, scratch_pool),
+            svn_dirent_local_style(wcroot_abspath, scratch_pool));
+        }
+    }
 
   SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, &repos_relpath,
                                &old_repos_root, &uuid,
@@ -112,48 +140,7 @@ svn_wc_relocate4(svn_wc_context_t *wc_ct
 
   SVN_ERR(validator(validator_baton, uuid, to, new_repos_root, scratch_pool));
 
-  /* ### FIXME: This will ultimately cause the DAV cache to be
-     recursively cleared, which is great in the recursive case, but
-     overreaching otherwise.  Granted, this only affects performance,
-     and that only for DAV RA implementations that rely on the DAV
-     cache. */
-  SVN_ERR(svn_wc__db_global_relocate(wc_ctx->db, local_abspath, new_repos_root,
-                                     scratch_pool));
-
-  if (!recurse)
-    {
-      /* This gets sticky.  We need to do the above relocation, and then
-         relocate each of the children *back* to the original location.  Ugh.
-       */
-      const apr_array_header_t *children;
-      apr_pool_t *iterpool;
-      int i;
-
-      SVN_ERR(svn_wc__db_read_children(&children, wc_ctx->db, local_abspath,
-                                       scratch_pool, scratch_pool));
-      iterpool = svn_pool_create(scratch_pool);
-      for (i = 0; i < children->nelts; i++)
-        {
-          const char *child = APR_ARRAY_IDX(children, i, const char *);
-          const char *child_abspath;
-          const char *child_from;
-          const char *child_to;
-
-          svn_pool_clear(iterpool);
-          child_abspath = svn_dirent_join(local_abspath, child, iterpool);
-
-          /* We invert the "from" and "to" because we're switching the
-             children back to the original location. */
-          child_from = svn_uri_join(to, child, iterpool);
-          child_to = svn_uri_join(from, child, iterpool);
-
-          SVN_ERR(svn_wc_relocate4(wc_ctx, child_abspath, child_from,
-                                   child_to, TRUE, validator, validator_baton,
-                                   iterpool));
-        }
-
-      svn_pool_destroy(iterpool);
-    }
-
-  return SVN_NO_ERROR;
+  return svn_error_return(svn_wc__db_global_relocate(wc_ctx->db, local_abspath,
+                                                     new_repos_root,
+                                                     scratch_pool));
 }