You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2011/05/08 05:36:04 UTC

svn commit: r1100676 - in /subversion/trunk/subversion/libsvn_ra_neon: commit.c merge.c

Author: cmpilato
Date: Sun May  8 03:36:03 2011
New Revision: 1100676

URL: http://svn.apache.org/viewvc?rev=1100676&view=rev
Log:
Add HTTPv2 support to ra_neon's commit process.  This shaves just
under 15,000 requests -- about 11% -- off of comparative runs of our
test suite before and after the change.

* subversion/libsvn_ra_neon/commit.c
  (version_rsrc_t): Remove 'local_path' variable.
  (commit_ctx_t): Add 'anchor_relpath', 'txn_url', and 'txn_root_url'
    members.
  (USING_HTTPV2_COMMIT_SUPPORT): New #define.
  (resource_baton_t): Add 'txn_root_url', 'base_revision', and
    'local_relpath' members.
  (dup_resource): Lose dup of 'local_path' structure member.
  (get_version_url): Add 'local_relpath' parameter, passed to the
    get_wc_prop() and push_wc_prop() callbacks.
  (add_child): Add 'parent_local_relpath' parameter, used in an
    updated call to get_version_url().  No longer calculate
    'local_path' member for resource_baton_t structure.
  (checkout_resource): Add 'local_relpath' parameter, passed to
    get_version_url().
  (copy_resource): Lose 'is_dir' parameter, opting instead to always
    specify an infinite-depth copy.  Avoid baseline queries when
    HTTPv2 is in use.
  (do_propatch): Lose 'ras' and 'rsrc' parameters, and PROPPATCH the
    resource's transaction root URL when using HTTPv2.
  (get_child_tokens): Move this function up with other help functions.
  (apply_revprops): Lose 'revprop_table' parameter, accessing that
    table via the commit_context_t instead.  Also, use HTTPv2
    semantics where available.
  (delete_activity): Remove as unused.
  (commit_open_root, commit_delete_entry, commit_add_dir,
   commit_open_dir, commit_change_dir_prop, commit_close_dir,
   commit_add_file, commit_open_file, commit_change_file_prop,
   commit_close_file, commit_close_edit): Update to use HTTPv2
    semantics where available, calculating any required baton data
    used in such scenarios.  Update calls to helper functions.
  (svn_ra_neon__get_commit_editor): Calculate the new 'anchor_relpath'
    commit context baton member.

* subversion/libsvn_ra_neon/merge.c
  (svn_ra_neon__merge_activity): Add a FIXME note.

Modified:
    subversion/trunk/subversion/libsvn_ra_neon/commit.c
    subversion/trunk/subversion/libsvn_ra_neon/merge.c

Modified: subversion/trunk/subversion/libsvn_ra_neon/commit.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_neon/commit.c?rev=1100676&r1=1100675&r2=1100676&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_neon/commit.c (original)
+++ subversion/trunk/subversion/libsvn_ra_neon/commit.c Sun May  8 03:36:03 2011
@@ -65,9 +65,6 @@ typedef struct version_rsrc_t
                              locally; NULL if this is a just-added resource */
   const char *wr_url;     /* working resource URL for this resource;
                              NULL for resources not (yet) checked out */
-  const char *local_path; /* path relative to the root of the commit
-                             (used for the get_wc_prop() and push_wc_prop()
-                             callbacks). */
   const char *name;       /* basename of the resource */
   apr_pool_t *pool;       /* pool in which this resource is allocated */
 
@@ -81,7 +78,9 @@ typedef struct commit_ctx_t
 
   /* Pointer to the RA session baton. */
   svn_ra_neon__session_t *ras;
-  const char *activity_url;
+
+  /* Commit anchor repository relpath. */
+  const char *anchor_relpath;
 
   /* A hash of revision properties (log messages, etc.) we need to set
      on the commit transaction. */
@@ -103,8 +102,20 @@ typedef struct commit_ctx_t
   apr_hash_t *lock_tokens;
   svn_boolean_t keep_locks;
 
+  /* HTTP v2 stuff */
+  const char *txn_url;           /* txn URL (!svn/txn/TXN_NAME) */
+  const char *txn_root_url;      /* commit anchor txn root URL */
+
+  /* HTTP v1 stuff (only valid when 'txn_url' is NULL) */
+  const char *activity_url;      /* activity base URL... */
+
 } commit_ctx_t;
 
+
+/* Are we using HTTPv2 semantics for this commit? */
+#define USING_HTTPV2_COMMIT_SUPPORT(commit_ctx) ((commit_ctx)->txn_url != NULL)
+
+
 typedef struct put_baton_t
 {
   apr_file_t *tmpfile;        /* may be NULL for content-less file */
@@ -119,6 +130,7 @@ typedef struct resource_baton_t
 {
   commit_ctx_t *cc;
   version_rsrc_t *rsrc;
+  svn_revnum_t base_revision; /* base revision */
   apr_hash_t *prop_changes; /* name/values pairs of new/changed properties. */
   apr_array_header_t *prop_deletes; /* names of properties to delete. */
   svn_boolean_t created; /* set if this is an add rather than an update */
@@ -126,6 +138,10 @@ typedef struct resource_baton_t
   apr_pool_t *pool; /* the pool from open_foo() / add_foo() */
   put_baton_t *put_baton;  /* baton for this file's PUT request */
   const char *token;       /* file's lock token, if available */
+  const char *txn_root_url; /* URL under !svn/txr/ (HTTPv2 only) */
+  const char *local_relpath; /* path relative to the root of the commit
+                                (used for the get_wc_prop() and push_wc_prop()
+                                callbacks). */
 } resource_baton_t;
 
 /* this property will be fetched from the server when we don't find it
@@ -149,34 +165,16 @@ static version_rsrc_t * dup_resource(ver
     apr_pstrdup(pool, base->vsn_url) : NULL;
   rsrc->wr_url = base->wr_url ?
     apr_pstrdup(pool, base->wr_url) : NULL;
-  rsrc->local_path = base->local_path ?
-    apr_pstrdup(pool, base->local_path) : NULL;
   rsrc->name = base->name ?
     apr_pstrdup(pool, base->name) : NULL;
   rsrc->pool = pool;
   return rsrc;
 }
 
-static svn_error_t * delete_activity(void *edit_baton,
-                                     apr_pool_t *pool)
-{
-  commit_ctx_t *cc = edit_baton;
-
-  /* If we started an activity, we need to (try to) delete it.  (If it
-     doesn't exist, that's okay, too.) */
-  if (cc->activity_url)
-    SVN_ERR(svn_ra_neon__simple_request(NULL, cc->ras, "DELETE",
-                                        cc->activity_url, NULL, NULL,
-                                        204 /* No Content */,
-                                        404 /* Not Found */, pool));
-
-  return SVN_NO_ERROR;
-}
-
-
 /* Get the version resource URL for RSRC, storing it in
    RSRC->vsn_url.  Use POOL for all temporary allocations. */
 static svn_error_t * get_version_url(commit_ctx_t *cc,
+                                     const char *local_relpath,
                                      const version_rsrc_t *parent,
                                      version_rsrc_t *rsrc,
                                      svn_boolean_t force,
@@ -193,7 +191,7 @@ static svn_error_t * get_version_url(com
           const svn_string_t *vsn_url_value;
 
           SVN_ERR(cc->ras->callbacks->get_wc_prop(cc->ras->callback_baton,
-                                                  rsrc->local_path,
+                                                  local_relpath,
                                                   SVN_RA_NEON__LP_VSN_URL,
                                                   &vsn_url_value,
                                                   pool));
@@ -264,7 +262,7 @@ static svn_error_t * get_version_url(com
     {
       /* Now we can store the new version-url. */
       SVN_ERR(cc->ras->callbacks->push_wc_prop(cc->ras->callback_baton,
-                                               rsrc->local_path,
+                                               local_relpath,
                                                SVN_RA_NEON__LP_VSN_URL,
                                                url_str,
                                                pool));
@@ -361,6 +359,7 @@ static svn_error_t * create_activity(com
 static svn_error_t * add_child(version_rsrc_t **child,
                                commit_ctx_t *cc,
                                const version_rsrc_t *parent,
+                               const char *parent_local_relpath,
                                const char *name,
                                int created,
                                svn_revnum_t revision,
@@ -380,7 +379,6 @@ static svn_error_t * add_child(version_r
   rsrc->revision = revision;
   rsrc->name = name;
   rsrc->url = svn_path_url_add_component2(parent->url, name, pool);
-  rsrc->local_path = svn_relpath_join(parent->local_path, name, pool);
 
   /* Case 1:  the resource is truly "new".  Either it was added as a
      completely new object, or implicitly created via a COPY.  Either
@@ -388,13 +386,18 @@ static svn_error_t * add_child(version_r
      URL by the rules of deltaV:  "copy structure is preserved below
      the WR you COPY to."  */
   if (created || (parent->vsn_url == NULL))
-    rsrc->wr_url = svn_path_url_add_component2(parent->wr_url, name, pool);
-
+    {
+      rsrc->wr_url = svn_path_url_add_component2(parent->wr_url, name, pool);
+    }
   /* Case 2: the resource is already under version-control somewhere.
      This means it has a VR URL already, and the WR URL won't exist
      until it's "checked out". */
   else
-    SVN_ERR(get_version_url(cc, parent, rsrc, FALSE, pool));
+    {
+      SVN_ERR(get_version_url(cc, svn_relpath_join(parent_local_relpath,
+                                                   name, pool),
+                              parent, rsrc, FALSE, pool));
+    }
 
   *child = rsrc;
   return SVN_NO_ERROR;
@@ -465,6 +468,7 @@ static svn_error_t * do_checkout(commit_
 
 
 static svn_error_t * checkout_resource(commit_ctx_t *cc,
+                                       const char *local_relpath,
                                        version_rsrc_t *rsrc,
                                        svn_boolean_t allow_404,
                                        const char *token,
@@ -492,7 +496,7 @@ static svn_error_t * checkout_resource(c
       locn = NULL;
 
       /* re-fetch, forcing a query to the server */
-      SVN_ERR(get_version_url(cc, NULL, rsrc, TRUE, pool));
+      SVN_ERR(get_version_url(cc, local_relpath, NULL, rsrc, TRUE, pool));
 
       /* do it again, but don't allow a 404 this time */
       err = do_checkout(cc, rsrc->vsn_url, FALSE, token,
@@ -510,7 +514,7 @@ static svn_error_t * checkout_resource(c
         return svn_error_createf
           (err->apr_err, err,
            _("File or directory '%s' is out of date; try updating"),
-           svn_relpath_local_style(rsrc->local_path, pool));
+           svn_relpath_local_style(local_relpath, pool));
       return err;
     }
 
@@ -565,101 +569,71 @@ static void record_prop_change(apr_pool_
 /* Send a Neon COPY request to the location identified by
    COPYFROM_PATH and COPYFROM_REVISION, using COPY_DST_URL as the
    "Destination" of that copy. */
-static svn_error_t *copy_resource(svn_ra_neon__session_t *ras,
-                                  const char *copyfrom_path,
-                                  svn_revnum_t copyfrom_revision,
-                                  const char *copy_dst_url,
-                                  svn_boolean_t is_dir,
-                                  apr_pool_t *scratch_pool)
-{
-  svn_string_t bc_url, bc_relative;
-  const char *copy_src_url;
-
-  /* Convert the copyfrom_* url/rev "public" pair into a Baseline
-     Collection (BC) URL that represents the revision -- and a
-     relative path under that BC.  */
-  SVN_ERR(svn_ra_neon__get_baseline_info(NULL, &bc_url, &bc_relative, NULL,
-                                         ras, copyfrom_path,
-                                         copyfrom_revision, scratch_pool));
+static svn_error_t * copy_resource(svn_ra_neon__session_t *ras,
+                                   const char *copyfrom_path,
+                                   svn_revnum_t copyfrom_revision,
+                                   const char *copy_dst_url,
+                                   apr_pool_t *scratch_pool)
+{
+  const char *baseline_coll_url, *baseline_relpath, *copy_src_url;
+  
+  if (SVN_RA_NEON__HAVE_HTTPV2_SUPPORT(ras))
+    {
+      baseline_coll_url = apr_psprintf(scratch_pool, "%s/%ld",
+                                       ras->rev_root_stub, copyfrom_revision);
+      baseline_relpath = svn_uri_is_child(ras->repos_root,
+                                          copyfrom_path, scratch_pool);
+      if (! baseline_relpath)
+        baseline_relpath = "";
+    }
+  else
+    {
+      svn_string_t bc_url, bc_relative;
 
+      /* Convert the copyfrom_* url/rev "public" pair into a Baseline
+         Collection (BC) URL that represents the revision -- and a
+         relative path under that BC.  */
+      SVN_ERR(svn_ra_neon__get_baseline_info(NULL, &bc_url, &bc_relative, NULL,
+                                             ras, copyfrom_path,
+                                             copyfrom_revision, scratch_pool));
+      baseline_coll_url = bc_url.data;
+      baseline_relpath = bc_relative.data;
+    }
+     
   /* Combine the BC-URL and relative path; this is the main
      "source" argument to the COPY request.  The "Destination:"
      header given to COPY is simply the wr_url that is already
      part of the file_baton. */
-  copy_src_url = svn_path_url_add_component2(bc_url.data,
-                                             bc_relative.data,
+  copy_src_url = svn_path_url_add_component2(baseline_coll_url,
+                                             baseline_relpath,
                                              scratch_pool);
 
   /* Have neon do the COPY. */
-  SVN_ERR(svn_ra_neon__copy(ras, 1 /* overwrite */,
-                            is_dir ? SVN_RA_NEON__DEPTH_INFINITE 
-                                   : SVN_RA_NEON__DEPTH_ZERO,
+  SVN_ERR(svn_ra_neon__copy(ras, 1 /* overwrite */, SVN_RA_NEON__DEPTH_INFINITE,
                             copy_src_url, copy_dst_url, scratch_pool));
   
   return SVN_NO_ERROR;
 }
 
-/*
-A very long note about enforcing directory-up-to-dateness when
-proppatching, writ by Ben:
-
-Once upon a time, I thought it would be necessary to attach the
-X-SVN-Version-Name header to every PROPPATCH request we send.  This
-would allow mod_dav_svn to verify that a directory is up-to-date.
-
-But it turns out that mod_dav_svn screams and errors if you *ever* try
-to CHECKOUT an out-of-date VR.  And furthermore, a directory is never
-a 'committable' (according to svn_client_commit) unless it has a
-propchange.  Therefore:
-
-1. when ra_neon's commit editor attempts to CHECKOUT a parent directory
-   because some child is being added or deleted, it's *unable* to get
-   the VR cache, and thus just gets the HEAD one instead.  So it ends
-   up always doing a CHECKOUT of the latest version of the directory.
-   This is actually fine; Subversion's semantics allow us to
-   add/delete children on out-of-date directories.  If, in dav terms,
-   this means always checking out the latest directory, so be it.  Any
-   namespace conflicts will be detected with the actual PUT or DELETE
-   of the child.
-
-2. when ra_neon's commit editor receives a directory propchange, it
-   *is* able to get the VR cache (because the dir is a "committable"),
-   and thus it does a CHECKOUT of the older directory.  And mod_dav_svn
-   will scream if the VR is out of date, which is exactly what we want in
-   the directory propchange scenario.
-
-The only potential badness here is the case of committing a directory
-with a propchange, and an add/rm of its child.  This commit should
-fail, due to the out-of-date propchange.  However, it's *possible*
-that it will fail for a different reason:  we might attempt the add/rm
-first, which means checking out the parent VR, which *would* be
-available from the cache, and thus we get an early error.  Instead of
-seeing an error about 'cannot proppatch out-of-date dir', the user
-will see an error about 'cannot checkout out-of-date parent'.  Not
-really a big deal I guess.
-
-*/
-static svn_error_t * do_proppatch(svn_ra_neon__session_t *ras,
-                                  const version_rsrc_t *rsrc,
-                                  resource_baton_t *rb,
+
+static svn_error_t * do_proppatch(resource_baton_t *rb,
                                   apr_pool_t *pool)
 {
-  const char *url = rsrc->wr_url;
-  apr_hash_t *extra_headers = NULL;
+  apr_hash_t *extra_headers = apr_hash_make(pool);
+  const char *proppatch_target =
+    USING_HTTPV2_COMMIT_SUPPORT(rb->cc) ? rb->txn_root_url : rb->rsrc->wr_url;
+
+  if (SVN_IS_VALID_REVNUM(rb->base_revision))
+    svn_ra_neon__set_header(extra_headers, SVN_DAV_VERSION_NAME_HEADER,
+                            apr_psprintf(pool, "%ld", rb->base_revision));
 
   if (rb->token)
-    {
-      const char *token_header_val;
-      token_header_val = apr_psprintf(pool, "(<%s>)", rb->token);
-
-      extra_headers = apr_hash_make(pool);
-      apr_hash_set(extra_headers, "If", APR_HASH_KEY_STRING,
-                   token_header_val);
-    }
+    apr_hash_set(extra_headers, "If", APR_HASH_KEY_STRING,
+                 apr_psprintf(pool, "(<%s>)", rb->token));
 
-  return svn_ra_neon__do_proppatch(ras, url, rb->prop_changes,
-                                   rb->prop_deletes, NULL, extra_headers,
-                                   pool);
+  return svn_ra_neon__do_proppatch(rb->cc->ras, proppatch_target,
+                                   rb->prop_changes, rb->prop_deletes,
+                                   NULL, extra_headers, pool);
 }
 
 
@@ -674,59 +648,99 @@ add_valid_target(commit_ctx_t *cc,
 }
 
 
-/* PROPPATCH the appropriate resource in order to set the properties
-   in REVPROP_TABLE on the commit transaction.  Use POOL for
-   temporary allocations.  */
+/* Helper func for commit_delete_entry.  Find all keys in LOCK_TOKENS
+   which are children of DIR.  Returns the keys (and their vals) in
+   CHILD_TOKENS.   No keys or values are reallocated or dup'd.  If no
+   keys are children, then return an empty hash.  Use POOL to allocate
+   new hash. */
+static apr_hash_t *get_child_tokens(apr_hash_t *lock_tokens,
+                                    const char *dir,
+                                    apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  apr_hash_t *tokens = apr_hash_make(pool);
+  apr_pool_t *subpool = svn_pool_create(pool);
+
+  for (hi = apr_hash_first(pool, lock_tokens); hi; hi = apr_hash_next(hi))
+    {
+      const void *key;
+      apr_ssize_t klen;
+      void *val;
+
+      svn_pool_clear(subpool);
+      apr_hash_this(hi, &key, &klen, &val);
+
+      if (svn_relpath_is_child(dir, key, subpool))
+        apr_hash_set(tokens, key, klen, val);
+    }
+
+  svn_pool_destroy(subpool);
+  return tokens;
+}
+
+
+/* PROPPATCH the appropriate resource in order to set the revision
+   properties in CC->REVPROP_TABLE on the commit transaction.  Use
+   POOL for temporary allocations.  */
 static svn_error_t *
 apply_revprops(commit_ctx_t *cc,
-               apr_hash_t *revprop_table,
                apr_pool_t *pool)
 {
-  const char *vcc;
-  version_rsrc_t vcc_rsrc = { SVN_INVALID_REVNUM };
-  svn_error_t *err = NULL;
-  int retry_count = 5;
-
-  /* ### this whole sequence can/should be replaced with an expand-property
-     ### REPORT when that is available on the server. */
-
-  /* fetch the DAV:version-controlled-configuration from the session's URL */
-  SVN_ERR(svn_ra_neon__get_vcc(&vcc, cc->ras, cc->ras->root.path, pool));
-
-
-  do {
-
-    svn_error_clear(err);
-
-    vcc_rsrc.pool = pool;
-    vcc_rsrc.vsn_url = vcc;
-
-    /* To set the revision properties, we must checkout the latest baseline
-       and get back a mutable "working" baseline.  */
-    err = checkout_resource(cc, &vcc_rsrc, FALSE, NULL, TRUE, pool);
-
-    /* There's a small chance of a race condition here, if apache is
-       experiencing heavy commit concurrency or if the network has
-       long latency.  It's possible that the value of HEAD changed
-       between the time we fetched the latest baseline and the time we
-       checkout that baseline.  If that happens, apache will throw us
-       a BAD_BASELINE error (deltaV says you can only checkout the
-       latest baseline).  We just ignore that specific error and
-       retry a few times, asking for the latest baseline again. */
-    if (err && err->apr_err != SVN_ERR_APMOD_BAD_BASELINE)
-      return err;
+  const char *proppatch_url;
+
+  if (USING_HTTPV2_COMMIT_SUPPORT(cc))
+    {
+      proppatch_url = cc->txn_url;
+    }
+  else
+    {
+      const char *vcc;
+      version_rsrc_t vcc_rsrc = { SVN_INVALID_REVNUM };
+      svn_error_t *err = NULL;
+      int retry_count = 5;
+
+      /* Fetch the DAV:version-controlled-configuration from the
+         session's URL.  */
+      SVN_ERR(svn_ra_neon__get_vcc(&vcc, cc->ras, cc->ras->root.path, pool));
+
+      do {
+        svn_error_clear(err);
+
+        vcc_rsrc.pool = pool;
+        vcc_rsrc.vsn_url = vcc;
+
+        /* To set the revision properties, we must checkout the latest
+           baseline and get back a mutable "working" baseline.  */
+        err = checkout_resource(cc, "", &vcc_rsrc, FALSE, NULL, TRUE, pool);
+
+        /* There's a small chance of a race condition here, if apache
+           is experiencing heavy commit concurrency or if the network
+           has long latency.  It's possible that the value of HEAD
+           changed between the time we fetched the latest baseline and
+           the time we checkout that baseline.  If that happens,
+           apache will throw us a BAD_BASELINE error (deltaV says you
+           can only checkout the latest baseline).  We just ignore
+           that specific error and retry a few times, asking for the
+           latest baseline again. */
+        if (err && err->apr_err != SVN_ERR_APMOD_BAD_BASELINE)
+          return err;
 
-  } while (err && (--retry_count > 0));
+      } while (err && (--retry_count > 0));
 
-  /* Yikes, if we couldn't hold onto HEAD after a few retries, throw a
-     real error.*/
-  if (err)
-    return err;
+      /* Yikes, if we couldn't hold onto HEAD after a few retries, throw a
+         real error.*/
+      if (err)
+        return err;
 
-  return svn_ra_neon__do_proppatch(cc->ras, vcc_rsrc.wr_url, revprop_table,
+      proppatch_url = vcc_rsrc.wr_url;
+    }
+
+  return svn_ra_neon__do_proppatch(cc->ras, proppatch_url, cc->revprop_table,
                                    NULL, NULL, NULL, pool);
 }
 
+
+/*** Commit Editor Functions ***/
 
 static svn_error_t * commit_open_root(void *edit_baton,
                                       svn_revnum_t base_revision,
@@ -735,66 +749,87 @@ static svn_error_t * commit_open_root(vo
 {
   commit_ctx_t *cc = edit_baton;
   resource_baton_t *root;
-  version_rsrc_t *rsrc;
-
-  /* Create a server-side activity which corresponds directly to an FS
-     transaction.  We will check out all further resources within the
-     context of this activity.  */
-  SVN_ERR(create_activity(cc, dir_pool));
-
-  /* Find the latest baseline resource, check it out, and then apply
-     the log message onto the thing. */
-  SVN_ERR(apply_revprops(cc, cc->revprop_table, dir_pool));
-
-  /* Create the root resource.  (We don't yet know the wr_url.) */
-  rsrc = apr_pcalloc(dir_pool, sizeof(*rsrc));
-  rsrc->pool = dir_pool;
-  rsrc->revision = base_revision;
-  rsrc->url = cc->ras->root.path;
-  rsrc->local_path = "";
-
-  SVN_ERR(get_version_url(cc, NULL, rsrc, FALSE, dir_pool));
+  version_rsrc_t *rsrc = NULL;
 
   root = apr_pcalloc(dir_pool, sizeof(*root));
   root->pool = dir_pool;
   root->cc = cc;
-  root->rsrc = rsrc;
+  root->base_revision = base_revision;
   root->created = FALSE;
+  root->local_relpath = "";
 
-  *root_baton = root;
-
-  return SVN_NO_ERROR;
-}
+  if (SVN_RA_NEON__HAVE_HTTPV2_SUPPORT(cc->ras))
+    {
+      /* POST to the 'me' resource to create our server-side
+         transaction (and, optionally, a corresponding activity). */
+      svn_ra_neon__request_t *req;
+      const char *header_val;
+
+      SVN_ERR(svn_ra_neon__request_create(&req, cc->ras, "POST",
+                                          cc->ras->me_resource, dir_pool));
+      ne_add_request_header(req->ne_req, "Content-Type", SVN_SKEL_MIME_TYPE);
+
+#ifdef SVN_NEON_SEND_VTXN_NAME
+      /* Enable this to exercise the VTXN-NAME code based on a client
+         supplied transaction name. */
+      ne_add_request_header(req->ne_req, SVN_DAV_VTXN_NAME_HEADER,
+                            svn_uuid_generate(dir_pool));
+#endif
+
+      SVN_ERR(svn_ra_neon__request_dispatch(NULL, req, NULL, "( create-txn )",
+                                            201, 0, dir_pool));
+
+      /* Check the response headers for either the virtual transaction
+         details, or the real transaction details.  We need to have
+         one or the other of those!  */
+      header_val = ne_get_response_header(req->ne_req,
+                                          SVN_DAV_VTXN_NAME_HEADER);
+      if (! header_val)
+        header_val = ne_get_response_header(req->ne_req,
+                                            SVN_DAV_TXN_NAME_HEADER);
+      if (! header_val)
+        return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+                                 _("POST request did not return transaction "
+                                   "information"));
+      cc->txn_url = svn_path_url_add_component2(cc->ras->txn_stub,
+                                                header_val, cc->pool);
+      cc->txn_root_url = svn_path_url_add_component2(cc->ras->txn_root_stub,
+                                                     header_val, cc->pool);
+
+      root->rsrc = NULL;
+      root->txn_root_url = svn_path_url_add_component2(cc->txn_root_url,
+                                                       cc->anchor_relpath,
+                                                       dir_pool);
+    }
+  else
+    {
+      /* Use MKACTIVITY against a unique child of activity collection
+         to create a server-side activity which corresponds directly
+         to an FS transaction.  We will check out all further
+         resources within the context of this activity. */
+      SVN_ERR(create_activity(cc, dir_pool));
+
+      /* Create the root resource.  (We don't yet know the wr_url.) */
+      rsrc = apr_pcalloc(dir_pool, sizeof(*rsrc));
+      rsrc->pool = dir_pool;
+      rsrc->revision = base_revision;
+      rsrc->url = cc->ras->root.path;
 
+      SVN_ERR(get_version_url(cc, root->local_relpath, NULL, rsrc,
+                              FALSE, dir_pool));
 
-/* Helper func for commit_delete_entry.  Find all keys in LOCK_TOKENS
-   which are children of DIR.  Returns the keys (and their vals) in
-   CHILD_TOKENS.   No keys or values are reallocated or dup'd.  If no
-   keys are children, then return an empty hash.  Use POOL to allocate
-   new hash. */
-static apr_hash_t *get_child_tokens(apr_hash_t *lock_tokens,
-                                    const char *dir,
-                                    apr_pool_t *pool)
-{
-  apr_hash_index_t *hi;
-  apr_hash_t *tokens = apr_hash_make(pool);
-  apr_pool_t *subpool = svn_pool_create(pool);
+      root->rsrc = rsrc;
+      root->txn_root_url = NULL;
+    }
 
-  for (hi = apr_hash_first(pool, lock_tokens); hi; hi = apr_hash_next(hi))
-    {
-      const void *key;
-      apr_ssize_t klen;
-      void *val;
+  /* Find the latest baseline resource, check it out, and then apply
+     the log message onto the thing. */
+  SVN_ERR(apply_revprops(cc, dir_pool));
 
-      svn_pool_clear(subpool);
-      apr_hash_this(hi, &key, &klen, &val);
 
-      if (svn_relpath_is_child(dir, key, subpool))
-        apr_hash_set(tokens, key, klen, val);
-    }
+  *root_baton = root;
 
-  svn_pool_destroy(subpool);
-  return tokens;
+  return SVN_NO_ERROR;
 }
 
 
@@ -805,32 +840,36 @@ static svn_error_t * commit_delete_entry
 {
   resource_baton_t *parent = parent_baton;
   const char *name = svn_relpath_basename(path, NULL);
-  apr_hash_t *extra_headers = NULL;
-  const char *child;
+  apr_hash_t *extra_headers = apr_hash_make(pool);
   int code;
   svn_error_t *serr;
+  const char *delete_target;
 
-  if (SVN_IS_VALID_REVNUM(revision))
+  if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
     {
-      const char *revstr = apr_psprintf(pool, "%ld", revision);
-
-      if (! extra_headers)
-        extra_headers = apr_hash_make(pool);
-
-      apr_hash_set(extra_headers, SVN_DAV_VERSION_NAME_HEADER,
-                   APR_HASH_KEY_STRING, revstr);
+      delete_target = svn_path_url_add_component2(parent->txn_root_url,
+                                                  name, pool);
     }
+  else
+    {
+      /* Get the URL to the working collection.  */
+      SVN_ERR(checkout_resource(parent->cc, parent->local_relpath,
+                                parent->rsrc, TRUE, NULL, FALSE, pool));
 
-  /* get the URL to the working collection */
-  SVN_ERR(checkout_resource(parent->cc, parent->rsrc, TRUE,
-                            NULL, FALSE, pool));
+      /* Create the URL for the child resource. */
+      delete_target = svn_path_url_add_component2(parent->rsrc->wr_url,
+                                                  name, pool);
+    }
 
-  /* create the URL for the child resource */
-  child = svn_path_url_add_component2(parent->rsrc->wr_url, name, pool);
+  /* If we have a base revision for the server to compare against,
+     pass that along in a custom header. */
+  if (SVN_IS_VALID_REVNUM(revision))
+    svn_ra_neon__set_header(extra_headers, SVN_DAV_VERSION_NAME_HEADER,
+                            apr_psprintf(pool, "%ld", revision));
 
-  /* Start out assuming that we're deleting a file;  try to lookup the
-     path itself in the token-hash, and if found, attach it to the If:
-     header. */
+  /* We start out assuming that we're deleting a file; try to lookup
+     the path itself in the token-hash, and if found, attach it to the
+     If: header. */
   if (parent->cc->lock_tokens)
     {
       const char *token =
@@ -856,15 +895,12 @@ static svn_error_t * commit_delete_entry
      header which prevents mod_dav_svn from actually doing the unlock. */
   if (parent->cc->keep_locks)
     {
-      if (! extra_headers)
-        extra_headers = apr_hash_make(pool);
-
       apr_hash_set(extra_headers, SVN_DAV_OPTIONS_HEADER,
                    APR_HASH_KEY_STRING, SVN_DAV_OPTION_KEEP_LOCKS);
     }
 
   serr = svn_ra_neon__simple_request(&code, parent->cc->ras,
-                                     "DELETE", child,
+                                     "DELETE", delete_target,
                                      extra_headers, NULL,
                                      204 /* No Content */,
                                      0, pool);
@@ -877,15 +913,17 @@ static svn_error_t * commit_delete_entry
                || (serr->apr_err == SVN_ERR_FS_LOCK_OWNER_MISMATCH)
                || (serr->apr_err == SVN_ERR_FS_PATH_ALREADY_LOCKED)))
     {
-      /* Re-attempt the DELETE request as if the path were a directory.
-         Discover all lock-tokens within the directory, and send them in
-         the body of the request (which is normally empty).  Of course,
-         if we don't *find* any additional lock-tokens, don't bother to
-         retry (it ain't gonna do any good).
+      /* Re-attempt the DELETE request as if the path were a
+         directory.  Discover all lock-tokens within the directory,
+         and send them in the body of the request (which is normally
+         empty).  Of course, if we don't *find* any additional
+         lock-tokens, don't bother to retry (it ain't gonna do any
+         good).
 
          Note that we're not sending the locks in the If: header, for
-         the same reason we're not sending in MERGE's headers: httpd has
-       limits on the amount of data it's willing to receive in headers. */
+         the same reason we're not sending in MERGE's headers: httpd
+         has limits on the amount of data it's willing to receive in
+         headers. */
 
       apr_hash_t *child_tokens = NULL;
       svn_ra_neon__request_t *request;
@@ -899,8 +937,7 @@ static svn_error_t * commit_delete_entry
 
       /* No kiddos?  Return the original error.  Else, clear it so it
          doesn't get leaked.  */
-      if ((! child_tokens)
-          || (apr_hash_count(child_tokens) == 0))
+      if ((! child_tokens) || (apr_hash_count(child_tokens) == 0))
         return serr;
       else
         svn_error_clear(serr);
@@ -912,7 +949,7 @@ static svn_error_t * commit_delete_entry
         apr_hash_set(child_tokens, path, APR_HASH_KEY_STRING, token);
 
       SVN_ERR(svn_ra_neon__request_create(&request, parent->cc->ras, "DELETE",
-                                          child, pool));
+                                          delete_target, pool));
 
       err = svn_ra_neon__assemble_locktoken_body(&locks_list,
                                                  child_tokens, request->pool);
@@ -954,34 +991,51 @@ static svn_error_t * commit_add_dir(cons
   const char *name = svn_relpath_basename(path, dir_pool);
   apr_pool_t *workpool = svn_pool_create(dir_pool);
   version_rsrc_t *rsrc = NULL;
-
-  /* check out the parent resource so that we can create the new collection
-     as one of its children. */
-  SVN_ERR(checkout_resource(parent->cc, parent->rsrc, TRUE,
-                            NULL, FALSE, dir_pool));
+  const char *mkcol_target = NULL;
 
   /* create a child object that contains all the resource urls */
   child = apr_pcalloc(dir_pool, sizeof(*child));
   child->pool = dir_pool;
+  child->base_revision = SVN_INVALID_REVNUM;
   child->cc = parent->cc;
   child->created = TRUE;
-  SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc,
-                    name, 1, SVN_INVALID_REVNUM, workpool));
-  child->rsrc = dup_resource(rsrc, dir_pool);
+  child->local_relpath = svn_relpath_join(parent->local_relpath,
+                                          name, dir_pool);
+
+  if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
+    {
+      child->rsrc = NULL;
+      child->txn_root_url = svn_path_url_add_component2(parent->txn_root_url,
+                                                        name, dir_pool);
+      mkcol_target = child->txn_root_url;
+    }
+  else
+    {
+      /* check out the parent resource so that we can create the new collection
+         as one of its children. */
+      SVN_ERR(checkout_resource(parent->cc, parent->local_relpath,
+                                parent->rsrc, TRUE, NULL, FALSE, dir_pool));
+      SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, parent->local_relpath,
+                        name, 1, SVN_INVALID_REVNUM, workpool));
+
+      child->rsrc = dup_resource(rsrc, dir_pool);
+      child->txn_root_url = NULL;
+      mkcol_target = child->rsrc->wr_url;
+    }
 
   if (! copyfrom_path)
     {
       /* This a new directory with no history, so just create a new,
          empty collection */
       SVN_ERR(svn_ra_neon__simple_request(&code, parent->cc->ras, "MKCOL",
-                                          child->rsrc->wr_url, NULL, NULL,
+                                          mkcol_target, NULL, NULL,
                                           201 /* Created */, 0, workpool));
     }
   else
     {
       /* This add has history, so we need to do a COPY. */
       SVN_ERR(copy_resource(parent->cc->ras, copyfrom_path, copyfrom_revision,
-                            child->rsrc->wr_url, TRUE, workpool));
+                            mkcol_target, workpool));
 
       /* Remember that this object was copied. */
       child->copied = TRUE;
@@ -1005,27 +1059,36 @@ static svn_error_t * commit_open_dir(con
   resource_baton_t *parent = parent_baton;
   resource_baton_t *child = apr_pcalloc(dir_pool, sizeof(*child));
   const char *name = svn_relpath_basename(path, dir_pool);
-  apr_pool_t *workpool = svn_pool_create(dir_pool);
   version_rsrc_t *rsrc = NULL;
 
   child->pool = dir_pool;
+  child->base_revision = base_revision;
   child->cc = parent->cc;
   child->created = FALSE;
+  child->local_relpath = svn_relpath_join(parent->local_relpath,
+                                          name, dir_pool);
 
-  SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc,
-                    name, 0, base_revision, workpool));
-  child->rsrc = dup_resource(rsrc, dir_pool);
+  if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
+    {
+      child->rsrc = NULL;
+      child->txn_root_url = svn_path_url_add_component2(parent->txn_root_url,
+                                                        name, dir_pool);
+    }
+  else
+    {
+      apr_pool_t *workpool = svn_pool_create(dir_pool);
 
-  /*
-  ** Note: open_dir simply means that a change has occurred somewhere
-  **       within this directory. We have nothing to do, to prepare for
-  **       those changes (each will be considered independently).
-  **
-  ** Note: if a directory is replaced by something else, then this callback
-  **       will not be used: a true replacement is modeled with a "delete"
-  **       followed by an "add".
-  */
-  svn_pool_destroy(workpool);
+      SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, parent->local_relpath,
+                        name, 0, base_revision, workpool));
+      child->rsrc = dup_resource(rsrc, dir_pool);
+      child->txn_root_url = NULL;
+
+      svn_pool_destroy(workpool);
+    }
+
+  /* We don't do any real work here -- open_dir() just sets up the
+     baton for this directory for use later when we operate on one of
+     its children. */
   *child_baton = child;
   return SVN_NO_ERROR;
 }
@@ -1038,14 +1101,18 @@ static svn_error_t * commit_change_dir_p
   resource_baton_t *dir = dir_baton;
 
   /* record the change. it will be applied at close_dir time. */
-  /* ### we should put this into the dir_baton's pool */
   record_prop_change(dir->pool, dir, name, value);
 
-  /* do the CHECKOUT sooner rather than later */
-  SVN_ERR(checkout_resource(dir->cc, dir->rsrc, TRUE, NULL, FALSE, pool));
+  if (! USING_HTTPV2_COMMIT_SUPPORT(dir->cc))
+    {
+      /* We might as well CHECKOUT now (if we haven't already).  Why
+         wait?  */
+      SVN_ERR(checkout_resource(dir->cc, dir->local_relpath, dir->rsrc,
+                                TRUE, NULL, FALSE, pool));
+    }
 
-  /* Add this path to the valid targets hash. */
-  add_valid_target(dir->cc, dir->rsrc->local_path, svn_nonrecursive);
+    /* Add the path to the valid targets hash. */
+  add_valid_target(dir->cc, dir->local_relpath, svn_nonrecursive);
 
   return SVN_NO_ERROR;
 }
@@ -1057,7 +1124,7 @@ static svn_error_t * commit_close_dir(vo
 
   /* Perform all of the property changes on the directory. Note that we
      checked out the directory when the first prop change was noted. */
-  return do_proppatch(dir->cc->ras, dir->rsrc, dir, pool);
+  return do_proppatch(dir, pool);
 }
 
 static svn_error_t * commit_add_file(const char *path,
@@ -1071,7 +1138,7 @@ static svn_error_t * commit_add_file(con
   resource_baton_t *file;
   const char *name = svn_relpath_basename(path, file_pool);
   apr_pool_t *workpool = svn_pool_create(file_pool);
-  version_rsrc_t *rsrc = NULL;
+  const char *put_target = NULL;
 
   /*
   ** To add a new file into the repository, we CHECKOUT the parent
@@ -1082,51 +1149,71 @@ static svn_error_t * commit_add_file(con
   ** method to copy into the working collection.
   */
 
-  /* Do the parent CHECKOUT first */
-  SVN_ERR(checkout_resource(parent->cc, parent->rsrc, TRUE,
-                            NULL, FALSE, workpool));
-
   /* Construct a file_baton that contains all the resource urls. */
   file = apr_pcalloc(file_pool, sizeof(*file));
+  file->base_revision = SVN_INVALID_REVNUM;
   file->pool = file_pool;
   file->cc = parent->cc;
   file->created = TRUE;
-  SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc,
-                    name, 1, SVN_INVALID_REVNUM, workpool));
-  file->rsrc = dup_resource(rsrc, file_pool);
+  file->local_relpath = svn_relpath_join(parent->local_relpath,
+                                         name, file_pool);
+
+  if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
+    {
+      file->rsrc = NULL;
+      file->txn_root_url = svn_path_url_add_component2(parent->txn_root_url,
+                                                       name, file_pool);
+      put_target = file->txn_root_url;
+    }
+  else
+    {
+      version_rsrc_t *rsrc = NULL;
+
+      /* Do the parent CHECKOUT first */
+      SVN_ERR(checkout_resource(parent->cc, parent->local_relpath, parent->rsrc,
+                                TRUE, NULL, FALSE, workpool));
+      SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, parent->local_relpath,
+                        name, 1, SVN_INVALID_REVNUM, workpool));
+
+      file->rsrc = dup_resource(rsrc, file_pool);
+      file->txn_root_url = NULL;
+      put_target = file->rsrc->wr_url;
+    }
+
   if (parent->cc->lock_tokens)
     file->token = apr_hash_get(parent->cc->lock_tokens, path,
                                APR_HASH_KEY_STRING);
 
-  /* If the parent directory existed before this commit then there may be a
-     file with this URL already. We need to ensure such a file does not
-     exist, which we do by attempting a PROPFIND in both public URL (the path
-     in HEAD) and the working URL (the path within the transaction), since
-     we cannot differentiate between deleted items.
+  /* If the parent directory existed before this commit then there may
+     be a file with this URL already. We need to ensure such a file
+     does not exist, which we do by attempting a PROPFIND in both
+     public URL (the path in HEAD) and the working URL (the path
+     within the transaction), since we cannot differentiate between
+     deleted items.
 
      ### For now, we'll assume that if this path has already been
-     added to the valid targets hash, that addition occurred during the
-     "delete" phase (if that's not the case, this editor is being
-     driven incorrectly, as we should never visit the same path twice
-     except in a delete+add situation). */
+     ### added to the valid targets hash, that addition occurred
+     ### during the "delete" phase (if that's not the case, this
+     ### editor is being driven incorrectly, as we should never visit
+     ### the same path twice except in a delete+add situation). */
   if ((! parent->created)
       && (! apr_hash_get(file->cc->valid_targets, path, APR_HASH_KEY_STRING)))
     {
       svn_ra_neon__resource_t *res;
-      svn_error_t *err1 = svn_ra_neon__get_starting_props(&res,
-                                                          file->cc->ras,
-                                                          file->rsrc->wr_url,
-                                                          NULL, workpool);
-      svn_error_t *err2 = svn_ra_neon__get_starting_props(&res,
-                                                          file->cc->ras,
-                                                          file->rsrc->url,
-                                                          NULL, workpool);
+      const char *public_url;
+      svn_error_t *err1, *err2;
+
+      public_url = svn_path_url_add_component2(file->cc->ras->url->data,
+                                               path, workpool);
+      err1 = svn_ra_neon__get_starting_props(&res, file->cc->ras,
+                                             put_target, NULL, workpool);
+      err2 = svn_ra_neon__get_starting_props(&res, file->cc->ras,
+                                             public_url, NULL, workpool);
       if (! err1 && ! err2)
         {
           /* If the PROPFINDs succeed the file already exists */
           return svn_error_createf(SVN_ERR_RA_DAV_ALREADY_EXISTS, NULL,
-                                   _("File '%s' already exists"),
-                                   file->rsrc->url);
+                                   _("File '%s' already exists"), path);
         }
       else if ((err1 && (err1->apr_err == SVN_ERR_FS_NOT_FOUND))
                || (err2 && (err2->apr_err == SVN_ERR_FS_NOT_FOUND)))
@@ -1145,17 +1232,15 @@ static svn_error_t * commit_add_file(con
     {
       /* This a truly new file. */
 
-      /* ### wait for apply_txdelta before doing a PUT. it might arrive a
-         ### "long time" from now. certainly after many other operations, so
-         ### we don't want to start a PUT just yet.
-         ### so... anything else to do here?
-      */
+      /* Wait for apply_txdelta() before doing a PUT.  It might arrive
+         a "long time" from now -- certainly after many other
+         operations -- we don't want to start a PUT just yet. */
     }
   else
     {
       /* This add has history, so we need to do a COPY. */
       SVN_ERR(copy_resource(parent->cc->ras, copyfrom_path, copyfrom_revision,
-                            file->rsrc->wr_url, FALSE, workpool));
+                            put_target, workpool));
 
       /* Remember that this object was copied. */
       file->copied = TRUE;
@@ -1180,31 +1265,46 @@ static svn_error_t * commit_open_file(co
   resource_baton_t *parent = parent_baton;
   resource_baton_t *file;
   const char *name = svn_relpath_basename(path, file_pool);
-  apr_pool_t *workpool = svn_pool_create(file_pool);
-  version_rsrc_t *rsrc = NULL;
 
   file = apr_pcalloc(file_pool, sizeof(*file));
   file->pool = file_pool;
+  file->base_revision = base_revision;
   file->cc = parent->cc;
   file->created = FALSE;
-  SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc,
-                    name, 0, base_revision, workpool));
-  file->rsrc = dup_resource(rsrc, file_pool);
+  file->local_relpath = svn_relpath_join(parent->local_relpath,
+                                         name, file_pool);
+
   if (parent->cc->lock_tokens)
     file->token = apr_hash_get(parent->cc->lock_tokens, path,
                                APR_HASH_KEY_STRING);
 
-  /* do the CHECKOUT now. we'll PUT the new file contents later on. */
-  SVN_ERR(checkout_resource(parent->cc, file->rsrc, TRUE,
-                            file->token, FALSE, workpool));
-
-  /* ### wait for apply_txdelta before doing a PUT. it might arrive a
-     ### "long time" from now. certainly after many other operations, so
-     ### we don't want to start a PUT just yet.
-     ### so... anything else to do here? what about the COPY case?
-  */
+  if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc))
+    {
+      file->rsrc = NULL;
+      file->txn_root_url = svn_path_url_add_component2(parent->txn_root_url,
+                                                       name, file_pool);
+    }
+  else
+    {
+      version_rsrc_t *rsrc = NULL;
+      apr_pool_t *workpool = svn_pool_create(file_pool);
+
+      SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, parent->local_relpath,
+                        name, 0, base_revision, workpool));
+      file->rsrc = dup_resource(rsrc, file_pool);
+      file->txn_root_url = NULL;
+
+      /* Do the CHECKOUT now.  */
+      SVN_ERR(checkout_resource(parent->cc, file->local_relpath, file->rsrc,
+                                TRUE, file->token, FALSE, workpool));
+
+      svn_pool_destroy(workpool);
+    }
+
+  /* Wait for apply_txdelta() before doing a PUT.  It might arrive
+     a "long time" from now -- certainly after many other
+     operations -- we don't want to start a PUT just yet. */
 
-  svn_pool_destroy(workpool);
   *file_baton = file;
   return SVN_NO_ERROR;
 }
@@ -1269,7 +1369,7 @@ commit_apply_txdelta(void *file_baton,
   svn_txdelta_to_svndiff2(handler, handler_baton, stream, 0, pool);
 
   /* Add this path to the valid targets hash. */
-  add_valid_target(file->cc, file->rsrc->local_path, svn_nonrecursive);
+  add_valid_target(file->cc, file->local_relpath, svn_nonrecursive);
 
   return SVN_NO_ERROR;
 }
@@ -1281,16 +1381,19 @@ static svn_error_t * commit_change_file_
 {
   resource_baton_t *file = file_baton;
 
-  /* record the change. it will be applied at close_file time. */
-  /* ### we should put this into the file_baton's pool */
+  /* Record the change.  It will be applied at close_file() time. */
   record_prop_change(file->pool, file, name, value);
 
-  /* do the CHECKOUT sooner rather than later */
-  SVN_ERR(checkout_resource(file->cc, file->rsrc, TRUE,
-                            file->token, FALSE, pool));
+  if (! USING_HTTPV2_COMMIT_SUPPORT(file->cc))
+    {
+      /* We might as well CHECKOUT now (if we haven't already).  Why
+         wait?  */
+      SVN_ERR(checkout_resource(file->cc, file->local_relpath, file->rsrc,
+                                TRUE, file->token, FALSE, pool));
+    }
 
-  /* Add this path to the valid targets hash. */
-  add_valid_target(file->cc, file->rsrc->local_path, svn_nonrecursive);
+    /* Add the path to the valid targets hash. */
+  add_valid_target(file->cc, file->local_relpath, svn_nonrecursive);
 
   return SVN_NO_ERROR;
 }
@@ -1314,26 +1417,27 @@ static svn_error_t * commit_close_file(v
 
   if (file->put_baton)
     {
+      svn_error_t *err = SVN_NO_ERROR;
       put_baton_t *pb = file->put_baton;
-      const char *url = file->rsrc->wr_url;
       apr_hash_t *extra_headers;
       svn_ra_neon__request_t *request;
-      svn_error_t *err = SVN_NO_ERROR;
+      const char *public_url =
+        svn_path_url_add_component2(file->cc->ras->url->data,
+                                    file->local_relpath, pool);
+      const char *put_target =
+        USING_HTTPV2_COMMIT_SUPPORT(cc) ? file->txn_root_url
+                                        : file->rsrc->wr_url;
 
       /* create/prep the request */
-      SVN_ERR(svn_ra_neon__request_create(&request, cc->ras, "PUT", url,
-                                          pool));
+      SVN_ERR(svn_ra_neon__request_create(&request, cc->ras, "PUT",
+                                          put_target, pool));
 
       extra_headers = apr_hash_make(request->pool);
 
       if (file->token)
-        svn_ra_neon__set_header
-          (extra_headers, "If",
-           apr_psprintf(pool, "<%s> (<%s>)",
-                        svn_path_url_add_component2(cc->ras->url->data,
-                                                    file->rsrc->url,
-                                                    request->pool),
-                        file->token));
+        svn_ra_neon__set_header(extra_headers, "If",
+                                apr_psprintf(pool, "<%s> (<%s>)",
+                                             public_url, file->token));
 
       if (pb->base_checksum)
         svn_ra_neon__set_header(extra_headers,
@@ -1345,6 +1449,11 @@ static svn_error_t * commit_close_file(v
                                 SVN_DAV_RESULT_FULLTEXT_MD5_HEADER,
                                 text_checksum);
 
+      if (SVN_IS_VALID_REVNUM(file->base_revision))
+        svn_ra_neon__set_header(extra_headers,
+                                SVN_DAV_VERSION_NAME_HEADER,
+                                apr_psprintf(pool, "%ld", file->base_revision));
+
       if (pb->tmpfile)
         {
           svn_ra_neon__set_header(extra_headers, "Content-Type",
@@ -1380,7 +1489,7 @@ static svn_error_t * commit_close_file(v
 
   /* Perform all of the property changes on the file. Note that we
      checked out the file when the first prop change was noted. */
-  return do_proppatch(cc->ras, file->rsrc, file, pool);
+  return do_proppatch(file, pool);
 }
 
 
@@ -1389,6 +1498,8 @@ static svn_error_t * commit_close_edit(v
 {
   commit_ctx_t *cc = edit_baton;
   svn_commit_info_t *commit_info = svn_create_commit_info(pool);
+  const char *merge_resource_url =
+    USING_HTTPV2_COMMIT_SUPPORT(cc) ? cc->txn_url : cc->activity_url;
 
   SVN_ERR(svn_ra_neon__merge_activity(&(commit_info->revision),
                                       &(commit_info->date),
@@ -1396,13 +1507,21 @@ static svn_error_t * commit_close_edit(v
                                       &(commit_info->post_commit_err),
                                       cc->ras,
                                       cc->ras->root.path,
-                                      cc->activity_url,
+                                      merge_resource_url,
                                       cc->valid_targets,
                                       cc->lock_tokens,
                                       cc->keep_locks,
                                       cc->ras->callbacks->push_wc_prop == NULL,
                                       pool));
-  SVN_ERR(delete_activity(edit_baton, pool));
+
+  /* DELETE any activity that might be left on the server. */
+  if (cc->activity_url)
+    {
+      SVN_ERR(svn_ra_neon__simple_request(NULL, cc->ras, "DELETE",
+                                          cc->activity_url, NULL, NULL,
+                                          204 /* No Content */,
+                                          404 /* Not Found */, pool));
+    }
 
   if (cc->callback && commit_info->revision != SVN_INVALID_REVNUM)
     SVN_ERR(cc->callback(commit_info, cc->callback_baton, pool));
@@ -1414,7 +1533,22 @@ static svn_error_t * commit_close_edit(v
 static svn_error_t * commit_abort_edit(void *edit_baton,
                                        apr_pool_t *pool)
 {
-  return delete_activity(edit_baton, pool);
+  commit_ctx_t *cc = edit_baton;
+  const char *delete_target = NULL;
+
+  /* If we started an activity and/or transaction, we need to (try to)
+     delete it.  (If it doesn't exist, that's okay, too.) */
+  if (USING_HTTPV2_COMMIT_SUPPORT(cc))
+    delete_target = cc->txn_url;
+  else
+    delete_target = cc->activity_url;
+
+  if (delete_target)
+    SVN_ERR(svn_ra_neon__simple_request(NULL, cc->ras, "DELETE",
+                                        delete_target, NULL, NULL,
+                                        204 /* No Content */,
+                                        404 /* Not Found */, pool));
+  return SVN_NO_ERROR;
 }
 
 
@@ -1457,6 +1591,11 @@ svn_error_t * svn_ra_neon__get_commit_ed
                    svn_string_dup(val, pool));
     }
 
+  /* Calculate the commit anchor's repository relpath. */
+  SVN_ERR(svn_ra_neon__get_path_relative_to_root(session,
+                                                 &(cc->anchor_relpath),
+                                                 ras->url->data, pool));
+
   /* Set up the editor. */
   commit_editor = svn_delta_default_editor(pool);
   commit_editor->open_root = commit_open_root;

Modified: subversion/trunk/subversion/libsvn_ra_neon/merge.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_neon/merge.c?rev=1100676&r1=1100675&r2=1100676&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_neon/merge.c (original)
+++ subversion/trunk/subversion/libsvn_ra_neon/merge.c Sun May  8 03:36:03 2011
@@ -683,7 +683,11 @@ svn_error_t * svn_ra_neon__assemble_lock
 }
 
 
-
+/* ### FIXME: As of HTTPv2, this isn't necessarily merging an
+   ### "activity".  It might be merging a transaction.  So,
+   ### ACTIVITY_URL might be a transaction root URL, not an actual
+   ### activity URL, etc.  Probably should rename ACTIVITY_URL to
+   ### MERGE_RESOURCE_URL or something.  */
 svn_error_t * svn_ra_neon__merge_activity(svn_revnum_t *new_rev,
                                           const char **committed_date,
                                           const char **committed_author,