diff options
Diffstat (limited to 'subversion/libsvn_ra_neon/commit.c')
-rw-r--r-- | subversion/libsvn_ra_neon/commit.c | 1631 |
1 files changed, 0 insertions, 1631 deletions
diff --git a/subversion/libsvn_ra_neon/commit.c b/subversion/libsvn_ra_neon/commit.c deleted file mode 100644 index ce05b60..0000000 --- a/subversion/libsvn_ra_neon/commit.c +++ /dev/null @@ -1,1631 +0,0 @@ -/* - * commit.c : routines for committing changes to the server - * - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - */ - - - -#include <apr_pools.h> -#include <apr_hash.h> -#include <apr_uuid.h> - -#define APR_WANT_STDIO -#define APR_WANT_STRFUNC -#include <apr_want.h> - -#include "svn_pools.h" -#include "svn_error.h" -#include "svn_delta.h" -#include "svn_io.h" -#include "svn_ra.h" -#include "../libsvn_ra/ra_loader.h" -#include "svn_dirent_uri.h" -#include "svn_path.h" -#include "svn_xml.h" -#include "svn_dav.h" -#include "svn_props.h" - -#include "svn_private_config.h" - -#include "ra_neon.h" - - -#define APPLY_TO_VERSION "<D:apply-to-version/>" - -/* - * version_rsrc_t: identify the relevant pieces of a resource on the server - * - * NOTE: If you tweak this structure, please update dup_resource() to - * ensure that it continues to create complete deep copies! - */ -typedef struct version_rsrc_t -{ - svn_revnum_t revision; /* resource's revision, or SVN_INVALID_REVNUM - if it's new or is the HEAD */ - const char *url; /* public/viewable/original resource URL */ - const char *vsn_url; /* version resource URL that we stored - 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 *name; /* basename of the resource */ - apr_pool_t *pool; /* pool in which this resource is allocated */ - -} version_rsrc_t; - - -typedef struct commit_ctx_t -{ - /* Pool for the whole of the commit context. */ - apr_pool_t *pool; - - /* Pointer to the RA session baton. */ - svn_ra_neon__session_t *ras; - - /* Commit anchor repository relpath. */ - const char *anchor_relpath; - - /* A hash of revision properties (log messages, etc.) we need to set - on the commit transaction. */ - apr_hash_t *revprop_table; - - /* A hash mapping svn_string_t * paths (those which are valid as - target in the MERGE response) to svn_node_kind_t kinds. */ - apr_hash_t *valid_targets; - - /* The (potential) author of this commit. */ - const char *user; - - /* The commit callback and baton */ - svn_commit_callback2_t callback; - void *callback_baton; - - /* The hash of lock tokens owned by the working copy, and whether - or not to keep them after this commit is complete. */ - 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 */ - svn_stringbuf_t *fname; /* may be NULL for content-less file */ - const char *base_checksum; /* hex md5 of base text; may be null */ - apr_off_t progress; - svn_ra_neon__session_t *ras; - apr_pool_t *pool; -} put_baton_t; - -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 */ - svn_boolean_t copied; /* set if this object was copied */ - 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 - cached in the WC property store. */ -static const ne_propname fetch_props[] = -{ - { "DAV:", "checked-in" }, - { NULL } -}; - -static const ne_propname log_message_prop = { SVN_DAV_PROP_NS_SVN, "log" }; - -/* Return a deep copy of BASE allocated from POOL. */ -static version_rsrc_t * dup_resource(version_rsrc_t *base, apr_pool_t *pool) -{ - version_rsrc_t *rsrc = apr_pcalloc(pool, sizeof(*rsrc)); - rsrc->revision = base->revision; - rsrc->url = base->url ? - apr_pstrdup(pool, base->url) : NULL; - rsrc->vsn_url = base->vsn_url ? - apr_pstrdup(pool, base->vsn_url) : NULL; - rsrc->wr_url = base->wr_url ? - apr_pstrdup(pool, base->wr_url) : NULL; - rsrc->name = base->name ? - apr_pstrdup(pool, base->name) : NULL; - rsrc->pool = pool; - return rsrc; -} - -/* 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, - apr_pool_t *pool) -{ - svn_ra_neon__resource_t *propres; - const char *url; - const svn_string_t *url_str; - - if (!force) - { - if (cc->ras->callbacks->get_wc_prop != NULL) - { - const svn_string_t *vsn_url_value; - - SVN_ERR(cc->ras->callbacks->get_wc_prop(cc->ras->callback_baton, - local_relpath, - SVN_RA_NEON__LP_VSN_URL, - &vsn_url_value, - pool)); - if (vsn_url_value != NULL) - { - rsrc->vsn_url = apr_pstrdup(rsrc->pool, vsn_url_value->data); - return SVN_NO_ERROR; - } - } - - /* If we know the version resource URL of the parent and it is - the same revision as RSRC, use that as a base to calculate - the version resource URL of RSRC. */ - if (parent && parent->vsn_url && parent->revision == rsrc->revision) - { - rsrc->vsn_url = svn_path_url_add_component2(parent->vsn_url, - rsrc->name, - rsrc->pool); - return SVN_NO_ERROR; - } - - /* whoops. it wasn't there. go grab it from the server. */ - } - - if (rsrc->revision == SVN_INVALID_REVNUM) - { - /* We aren't trying to get a specific version -- use the HEAD. We - fetch the version URL from the public URL. */ - url = rsrc->url; - } - else - { - const char *bc_url; - const char *bc_relative; - - /* The version URL comes from a resource in the Baseline Collection. */ - SVN_ERR(svn_ra_neon__get_baseline_info(&bc_url, &bc_relative, NULL, - cc->ras, rsrc->url, - rsrc->revision, pool)); - url = svn_path_url_add_component2(bc_url, bc_relative, pool); - } - - /* Get the DAV:checked-in property, which contains the URL of the - Version Resource */ - SVN_ERR(svn_ra_neon__get_props_resource(&propres, cc->ras, url, - NULL, fetch_props, pool)); - url_str = apr_hash_get(propres->propset, - SVN_RA_NEON__PROP_CHECKED_IN, - APR_HASH_KEY_STRING); - if (url_str == NULL) - { - /* ### need a proper SVN_ERR here */ - return svn_error_create(APR_EGENERAL, NULL, - _("Could not fetch the Version Resource URL " - "(needed during an import or when it is " - "missing from the local, cached props)")); - } - - /* ensure we get the proper lifetime for this URL since it is going into - a resource object. */ - rsrc->vsn_url = apr_pstrdup(rsrc->pool, url_str->data); - - if (cc->ras->callbacks->push_wc_prop != NULL) - { - /* Now we can store the new version-url. */ - SVN_ERR(cc->ras->callbacks->push_wc_prop(cc->ras->callback_baton, - local_relpath, - SVN_RA_NEON__LP_VSN_URL, - url_str, - pool)); - } - - return SVN_NO_ERROR; -} - -/* When FORCE is true, then we force a query to the server, ignoring any - cached property. */ -static svn_error_t * get_activity_collection(commit_ctx_t *cc, - const svn_string_t **collection, - svn_boolean_t force, - apr_pool_t *pool) -{ - if (!force && cc->ras->callbacks->get_wc_prop != NULL) - { - /* with a get_wc_prop, we can just ask for the activity URL from the - property store. */ - - /* get the URL where we should create activities */ - SVN_ERR(cc->ras->callbacks->get_wc_prop(cc->ras->callback_baton, - "", - SVN_RA_NEON__LP_ACTIVITY_COLL, - collection, - pool)); - - if (*collection != NULL) - { - /* the property was there. return it. */ - return SVN_NO_ERROR; - } - - /* property not found for some reason. get it from the server. */ - } - - /* use our utility function to fetch the activity URL */ - SVN_ERR(svn_ra_neon__get_activity_collection(collection, - cc->ras, - pool)); - - if (cc->ras->callbacks->push_wc_prop != NULL) - { - /* save the (new) activity collection URL into the directory */ - SVN_ERR(cc->ras->callbacks->push_wc_prop(cc->ras->callback_baton, - "", - SVN_RA_NEON__LP_ACTIVITY_COLL, - *collection, - pool)); - } - - return SVN_NO_ERROR; -} - -static svn_error_t * create_activity(commit_ctx_t *cc, - apr_pool_t *pool) -{ - const svn_string_t * activity_collection; - const char *uuid_buf = svn_uuid_generate(pool); - int code; - const char *url; - - /* get the URL where we'll create activities, construct the URL for - the activity, and create the activity. The URL for our activity - will be ACTIVITY_COLL/UUID */ - SVN_ERR(get_activity_collection(cc, &activity_collection, FALSE, pool)); - url = svn_path_url_add_component2(activity_collection->data, - uuid_buf, pool); - SVN_ERR(svn_ra_neon__simple_request(&code, cc->ras, - "MKACTIVITY", url, NULL, NULL, - 201 /* Created */, - 404 /* Not Found */, pool)); - - /* if we get a 404, then it generally means that the cached activity - collection no longer exists. Retry the sequence, but force a query - to the server for the activity collection. */ - if (code == 404) - { - SVN_ERR(get_activity_collection(cc, &activity_collection, TRUE, pool)); - url = svn_path_url_add_component2(activity_collection->data, - uuid_buf, pool); - SVN_ERR(svn_ra_neon__simple_request(&code, cc->ras, - "MKACTIVITY", url, NULL, NULL, - 201, 0, pool)); - } - - cc->activity_url = apr_pstrdup(cc->pool, url); - - return SVN_NO_ERROR; -} - -/* Add a child resource. POOL should be as "temporary" as possible, - but probably not as far as requiring a new temp pool. */ -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, - apr_pool_t *pool) -{ - version_rsrc_t *rsrc; - - /* ### todo: This from Yoshiki Hayashi <yoshiki@xemacs.org>: - - Probably created flag in add_child can be removed because - revision is valid => created is false - revision is invalid => created is true - */ - - rsrc = apr_pcalloc(pool, sizeof(*rsrc)); - rsrc->pool = pool; - rsrc->revision = revision; - rsrc->name = name; - rsrc->url = svn_path_url_add_component2(parent->url, 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 - way, it has no VR URL anywhere. However, we *can* derive its WR - 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); - } - /* 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, svn_relpath_join(parent_local_relpath, - name, pool), - parent, rsrc, FALSE, pool)); - } - - *child = rsrc; - return SVN_NO_ERROR; -} - - -static svn_error_t * do_checkout(commit_ctx_t *cc, - const char *vsn_url, - svn_boolean_t allow_404, - const char *token, - svn_boolean_t is_vcc, - int *code, - const char **locn, - apr_pool_t *pool) -{ - svn_ra_neon__request_t *request; - const char *body; - apr_hash_t *extra_headers = NULL; - svn_error_t *err = SVN_NO_ERROR; - - /* assert: vsn_url != NULL */ - - /* ### send a CHECKOUT resource on vsn_url; include cc->activity_url; - ### place result into res->wr_url and return it */ - - /* create/prep the request */ - SVN_ERR(svn_ra_neon__request_create(&request, cc->ras, "CHECKOUT", vsn_url, - pool)); - - /* ### store this into cc to avoid pool growth */ - body = apr_psprintf(request->pool, - "<?xml version=\"1.0\" encoding=\"utf-8\"?>" - "<D:checkout xmlns:D=\"DAV:\">" - "<D:activity-set>" - "<D:href>%s</D:href>" - "</D:activity-set>%s</D:checkout>", - cc->activity_url, - is_vcc ? APPLY_TO_VERSION: ""); - - if (token) - { - extra_headers = apr_hash_make(request->pool); - svn_ra_neon__set_header(extra_headers, "If", - apr_psprintf(request->pool, "(<%s>)", token)); - } - - /* run the request and get the resulting status code (and svn_error_t) */ - err = svn_ra_neon__request_dispatch(code, request, extra_headers, body, - 201 /* Created */, - allow_404 ? 404 /* Not Found */ : 0, - pool); - if (err) - goto cleanup; - - if (allow_404 && *code == 404 && request->err) - { - svn_error_clear(request->err); - request->err = SVN_NO_ERROR; - } - - *locn = svn_ra_neon__request_get_location(request, pool); - - cleanup: - svn_ra_neon__request_destroy(request); - - return err; -} - - -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, - svn_boolean_t is_vcc, - apr_pool_t *pool) -{ - int code; - const char *locn = NULL; - ne_uri parse; - svn_error_t *err; - - if (rsrc->wr_url != NULL) - { - /* already checked out! */ - return NULL; - } - - /* check out the Version Resource */ - err = do_checkout(cc, rsrc->vsn_url, allow_404, token, - is_vcc, &code, &locn, pool); - - /* possibly run the request again, with a re-fetched Version Resource */ - if (err == NULL && allow_404 && code == 404) - { - locn = NULL; - - /* re-fetch, forcing a query to the server */ - 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, - is_vcc, &code, &locn, pool); - } - - /* special-case when conflicts occur */ - if (err) - { - /* ### TODO: it's a shame we don't have the full path from the - ### root of the drive here, nor the type of the resource. - ### Because we lack this information, the error message is - ### overly generic. See issue #2740. */ - if (err->apr_err == SVN_ERR_FS_CONFLICT) - return svn_error_createf - (SVN_ERR_FS_OUT_OF_DATE, err, - _("File or directory '%s' is out of date; try updating"), - local_relpath); - return err; - } - - /* we got the header, right? */ - if (locn == NULL) - return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, - _("The CHECKOUT response did not contain a " - "'Location:' header")); - - /* The location is an absolute URI. We want just the path portion. */ - /* ### what to do with the rest? what if it points somewhere other - ### than the current session? */ - if (ne_uri_parse(locn, &parse) != 0) - { - ne_uri_free(&parse); - return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, - _("Unable to parse URL '%s'"), locn); - } - - rsrc->wr_url = apr_pstrdup(rsrc->pool, parse.path); - ne_uri_free(&parse); - - return SVN_NO_ERROR; -} - -static void record_prop_change(apr_pool_t *pool, - resource_baton_t *r, - const char *name, - const svn_string_t *value) -{ - /* copy the name into the pool so we get the right lifetime (who knows - what the caller will do with it) */ - name = apr_pstrdup(pool, name); - - if (value) - { - /* changed/new property */ - if (r->prop_changes == NULL) - r->prop_changes = apr_hash_make(pool); - apr_hash_set(r->prop_changes, name, APR_HASH_KEY_STRING, - svn_string_dup(value, pool)); - } - else - { - /* deleted property. */ - if (r->prop_deletes == NULL) - r->prop_deletes = apr_array_make(pool, 5, sizeof(char *)); - APR_ARRAY_PUSH(r->prop_deletes, const char *) = name; - } -} - -/* 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, - apr_pool_t *scratch_pool) -{ - const char *bc_url; - const char *bc_relative; - const char *copy_src_url; - - SVN_ERR(svn_ra_neon__get_baseline_info(&bc_url, &bc_relative, NULL, - ras, copyfrom_path, - copyfrom_revision, scratch_pool)); - copy_src_url = svn_path_url_add_component2(bc_url, bc_relative, - scratch_pool); - 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; -} - - -static svn_error_t * do_proppatch(resource_baton_t *rb, - apr_pool_t *pool) -{ - 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) - apr_hash_set(extra_headers, "If", APR_HASH_KEY_STRING, - apr_psprintf(pool, "(<%s>)", rb->token)); - - return svn_error_trace(svn_ra_neon__do_proppatch(rb->cc->ras, - proppatch_target, - rb->prop_changes, - rb->prop_deletes, - NULL, extra_headers, - pool)); -} - - -static void -add_valid_target(commit_ctx_t *cc, - const char *path, - enum svn_recurse_kind kind) -{ - apr_hash_t *hash = cc->valid_targets; - svn_string_t *path_str = svn_string_create(path, apr_hash_pool_get(hash)); - apr_hash_set(hash, path_str->data, path_str->len, (void*)kind); -} - - -/* 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_pool_t *pool) -{ - 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)); - - /* Yikes, if we couldn't hold onto HEAD after a few retries, throw a - real error.*/ - if (err) - return err; - - proppatch_url = vcc_rsrc.wr_url; - } - - return svn_error_trace(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, - apr_pool_t *dir_pool, - void **root_baton) -{ - commit_ctx_t *cc = edit_baton; - resource_baton_t *root; - version_rsrc_t *rsrc = NULL; - - root = apr_pcalloc(dir_pool, sizeof(*root)); - root->pool = dir_pool; - root->cc = cc; - root->base_revision = base_revision; - root->created = FALSE; - root->local_relpath = ""; - - 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_error_t *err; - - 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_DAV_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 - - err = svn_ra_neon__request_dispatch(NULL, req, NULL, "( create-txn )", - 201, 0, dir_pool); - if (!err) - { - /* 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! */ - if ((header_val = ne_get_response_header(req->ne_req, - SVN_DAV_VTXN_NAME_HEADER))) - { - cc->txn_url = svn_path_url_add_component2(cc->ras->vtxn_stub, - header_val, cc->pool); - cc->txn_root_url - = svn_path_url_add_component2(cc->ras->vtxn_root_stub, - header_val, cc->pool); - } - else if ((header_val - = ne_get_response_header(req->ne_req, - SVN_DAV_TXN_NAME_HEADER))) - { - 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); - } - else - err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, - _("POST request did not return transaction " - "information")); - } - svn_ra_neon__request_destroy(req); - SVN_ERR(err); - - 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)); - - root->rsrc = rsrc; - root->txn_root_url = NULL; - } - - /* Find the latest baseline resource, check it out, and then apply - the log message onto the thing. */ - SVN_ERR(apply_revprops(cc, dir_pool)); - - - *root_baton = root; - - return SVN_NO_ERROR; -} - - -static svn_error_t * commit_delete_entry(const char *path, - svn_revnum_t revision, - void *parent_baton, - apr_pool_t *pool) -{ - resource_baton_t *parent = parent_baton; - const char *name = svn_relpath_basename(path, NULL); - apr_hash_t *extra_headers = apr_hash_make(pool); - int code; - svn_error_t *serr; - const char *delete_target; - - if (USING_HTTPV2_COMMIT_SUPPORT(parent->cc)) - { - 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)); - - /* Create the URL for the child resource. */ - delete_target = 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)); - - /* 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 = - apr_hash_get(parent->cc->lock_tokens, path, APR_HASH_KEY_STRING); - - if (token) - { - const char *token_header_val; - const char *token_uri; - - token_uri = svn_path_url_add_component2(parent->cc->ras->url->data, - path, pool); - token_header_val = apr_psprintf(pool, "<%s> (<%s>)", - token_uri, token); - extra_headers = apr_hash_make(pool); - apr_hash_set(extra_headers, "If", APR_HASH_KEY_STRING, - token_header_val); - } - } - - /* dav_method_delete() always calls dav_unlock(), but if the svn - client passed --no-unlock to 'svn commit', then we need to send a - header which prevents mod_dav_svn from actually doing the unlock. */ - if (parent->cc->keep_locks) - { - 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", delete_target, - extra_headers, NULL, - 204 /* No Content */, - 0, pool); - - /* A locking-related error most likely means we were deleting a - directory rather than a file, and didn't send all of the - necessary lock-tokens within the directory. */ - if (serr && ((serr->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN) - || (serr->apr_err == SVN_ERR_FS_NO_LOCK_TOKEN) - || (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). - - 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. */ - - apr_hash_t *child_tokens = NULL; - svn_ra_neon__request_t *request; - const char *body; - const char *token; - svn_stringbuf_t *locks_list; - svn_error_t *err = SVN_NO_ERROR; - - if (parent->cc->lock_tokens) - child_tokens = get_child_tokens(parent->cc->lock_tokens, path, pool); - - /* No kiddos? Return the original error. Else, clear it so it - doesn't get leaked. */ - if ((! child_tokens) || (apr_hash_count(child_tokens) == 0)) - return serr; - else - svn_error_clear(serr); - - /* In preparation of directory locks, go ahead and add the actual - target's lock token to those of its children. */ - if ((token = apr_hash_get(parent->cc->lock_tokens, path, - APR_HASH_KEY_STRING))) - { - /* ### copy PATH into the right pool. which? */ - apr_hash_set(child_tokens, path, APR_HASH_KEY_STRING, token); - } - - SVN_ERR(svn_ra_neon__request_create(&request, parent->cc->ras, "DELETE", - delete_target, pool)); - - err = svn_ra_neon__assemble_locktoken_body(&locks_list, - child_tokens, request->pool); - if (err) - goto cleanup; - - body = apr_psprintf(request->pool, - "<?xml version=\"1.0\" encoding=\"utf-8\"?> %s", - locks_list->data); - - err = svn_ra_neon__request_dispatch(&code, request, NULL, body, - 204 /* Created */, - 404 /* Not Found */, - pool); - cleanup: - svn_ra_neon__request_destroy(request); - SVN_ERR(err); - } - else if (serr) - return serr; - - /* Add this path to the valid targets hash. */ - add_valid_target(parent->cc, path, svn_nonrecursive); - - return SVN_NO_ERROR; -} - - -static svn_error_t * commit_add_dir(const char *path, - void *parent_baton, - const char *copyfrom_path, - svn_revnum_t copyfrom_revision, - apr_pool_t *dir_pool, - void **child_baton) -{ - resource_baton_t *parent = parent_baton; - resource_baton_t *child; - int code; - const char *name = svn_relpath_basename(path, dir_pool); - apr_pool_t *workpool = svn_pool_create(dir_pool); - version_rsrc_t *rsrc = NULL; - 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; - 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", - 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, - mkcol_target, workpool)); - - /* Remember that this object was copied. */ - child->copied = TRUE; - } - - /* Add this path to the valid targets hash. */ - add_valid_target(parent->cc, path, - copyfrom_path ? svn_recursive : svn_nonrecursive); - - svn_pool_destroy(workpool); - *child_baton = child; - return SVN_NO_ERROR; -} - -static svn_error_t * commit_open_dir(const char *path, - void *parent_baton, - svn_revnum_t base_revision, - apr_pool_t *dir_pool, - void **child_baton) -{ - 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); - 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); - - 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); - - 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; -} - -static svn_error_t * commit_change_dir_prop(void *dir_baton, - const char *name, - const svn_string_t *value, - apr_pool_t *pool) -{ - resource_baton_t *dir = dir_baton; - - /* record the change. it will be applied at close_dir time. */ - record_prop_change(dir->pool, dir, name, value); - - 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 the path to the valid targets hash. */ - add_valid_target(dir->cc, dir->local_relpath, svn_nonrecursive); - - return SVN_NO_ERROR; -} - -static svn_error_t * commit_close_dir(void *dir_baton, - apr_pool_t *pool) -{ - resource_baton_t *dir = dir_baton; - - /* 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, pool); -} - -static svn_error_t * commit_add_file(const char *path, - void *parent_baton, - const char *copyfrom_path, - svn_revnum_t copyfrom_revision, - apr_pool_t *file_pool, - void **file_baton) -{ - 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); - const char *put_target = NULL; - - /* - ** To add a new file into the repository, we CHECKOUT the parent - ** collection, then PUT the file as a member of the resulting working - ** collection. - ** - ** If the file was copied from elsewhere, then we will use the COPY - ** method to copy into the working collection. - */ - - /* 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; - 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. - - ### 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). */ - if ((! parent->created) - && (! apr_hash_get(file->cc->valid_targets, path, APR_HASH_KEY_STRING))) - { - static const ne_propname restype_props[] = - { - { "DAV:", "resourcetype" }, - { NULL } - }; - svn_ra_neon__resource_t *res; - 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_props_resource(&res, parent->cc->ras, put_target, - NULL, restype_props, workpool); - err2 = svn_ra_neon__get_props_resource(&res, parent->cc->ras, public_url, - NULL, restype_props, 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"), path); - } - else if ((err1 && (err1->apr_err == SVN_ERR_FS_NOT_FOUND)) - || (err2 && (err2->apr_err == SVN_ERR_FS_NOT_FOUND))) - { - svn_error_clear(err1); - svn_error_clear(err2); - } - else - { - /* A real error */ - return svn_error_compose_create(err1, err2); - } - } - - if (! copyfrom_path) - { - /* 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 -- 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, - put_target, workpool)); - - /* Remember that this object was copied. */ - file->copied = TRUE; - } - - /* Add this path to the valid targets hash. */ - add_valid_target(parent->cc, path, svn_nonrecursive); - - svn_pool_destroy(workpool); - - /* return the file_baton */ - *file_baton = file; - return SVN_NO_ERROR; -} - -static svn_error_t * commit_open_file(const char *path, - void *parent_baton, - svn_revnum_t base_revision, - apr_pool_t *file_pool, - void **file_baton) -{ - resource_baton_t *parent = parent_baton; - resource_baton_t *file; - const char *name = svn_relpath_basename(path, file_pool); - - file = apr_pcalloc(file_pool, sizeof(*file)); - file->pool = file_pool; - file->base_revision = base_revision; - file->cc = parent->cc; - file->created = FALSE; - 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); - - 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. */ - - *file_baton = file; - return SVN_NO_ERROR; -} - -static svn_error_t * commit_stream_write(void *baton, - const char *data, - apr_size_t *len) -{ - put_baton_t *pb = baton; - svn_ra_neon__session_t *ras = pb->ras; - apr_status_t status; - - if (ras->callbacks && ras->callbacks->cancel_func) - SVN_ERR(ras->callbacks->cancel_func(ras->callback_baton)); - - /* drop the data into our temp file */ - status = apr_file_write_full(pb->tmpfile, data, *len, NULL); - if (status) - return svn_error_wrap_apr(status, - _("Could not write svndiff to temp file")); - - if (ras->progress_func) - { - pb->progress += *len; - ras->progress_func(pb->progress, -1, ras->progress_baton, pb->pool); - } - - return SVN_NO_ERROR; -} - -static svn_error_t * -commit_apply_txdelta(void *file_baton, - const char *base_checksum, - apr_pool_t *pool, - svn_txdelta_window_handler_t *handler, - void **handler_baton) -{ - resource_baton_t *file = file_baton; - put_baton_t *baton; - svn_stream_t *stream; - - baton = apr_pcalloc(file->pool, sizeof(*baton)); - baton->ras = file->cc->ras; - baton->pool = file->pool; - file->put_baton = baton; - - if (base_checksum) - baton->base_checksum = apr_pstrdup(file->pool, base_checksum); - else - baton->base_checksum = NULL; - - /* ### oh, hell. Neon's request body support is either text (a C string), - ### or a FILE*. since we are getting binary data, we must use a FILE* - ### for now. isn't that special? */ - SVN_ERR(svn_io_open_unique_file3(&baton->tmpfile, NULL, NULL, - svn_io_file_del_on_pool_cleanup, - file->pool, pool)); - - stream = svn_stream_create(baton, pool); - svn_stream_set_write(stream, commit_stream_write); - - svn_txdelta_to_svndiff2(handler, handler_baton, stream, 0, pool); - - /* Add this path to the valid targets hash. */ - add_valid_target(file->cc, file->local_relpath, svn_nonrecursive); - - return SVN_NO_ERROR; -} - -static svn_error_t * commit_change_file_prop(void *file_baton, - const char *name, - const svn_string_t *value, - apr_pool_t *pool) -{ - resource_baton_t *file = file_baton; - - /* Record the change. It will be applied at close_file() time. */ - record_prop_change(file->pool, file, name, value); - - 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 the path to the valid targets hash. */ - add_valid_target(file->cc, file->local_relpath, svn_nonrecursive); - - return SVN_NO_ERROR; -} - -static svn_error_t * commit_close_file(void *file_baton, - const char *text_checksum, - apr_pool_t *pool) -{ - resource_baton_t *file = file_baton; - commit_ctx_t *cc = file->cc; - - /* If this is a newly added file, not copied, and the editor driver - didn't call apply_textdelta(), then we'll pretend they *did* call - apply_textdelta() and described a zero-byte empty file. */ - if ((! file->put_baton) && file->created && (! file->copied)) - { - /* Make a dummy put_baton, with NULL fields to indicate that - we're dealing with a content-less (zero-byte) file. */ - file->put_baton = apr_pcalloc(file->pool, sizeof(*(file->put_baton))); - } - - if (file->put_baton) - { - svn_error_t *err = SVN_NO_ERROR; - put_baton_t *pb = file->put_baton; - apr_hash_t *extra_headers; - svn_ra_neon__request_t *request; - int code; - 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", - 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>)", - public_url, file->token)); - - if (pb->base_checksum) - svn_ra_neon__set_header(extra_headers, - SVN_DAV_BASE_FULLTEXT_MD5_HEADER, - pb->base_checksum); - - if (text_checksum) - svn_ra_neon__set_header(extra_headers, - 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", - SVN_SVNDIFF_MIME_TYPE); - - /* Give the file to neon. The provider will rewind the file. */ - err = svn_ra_neon__set_neon_body_provider(request, pb->tmpfile); - if (err) - goto cleanup; - } - else - { - ne_set_request_body_buffer(request->ne_req, "", 0); - } - - /* run the request and get the resulting status code (and svn_error_t) */ - err = svn_ra_neon__request_dispatch(&code, request, extra_headers, NULL, - 201 /* Created */, - 204 /* No Content */, - pool); - - if (err && (err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)) - { - switch (code) - { - case 423: - err = svn_error_createf(SVN_ERR_RA_NOT_LOCKED, err, - _("No lock on path '%s'" - " (Status %d on PUT Request)"), - put_target, code); - default: - break; - } - } - cleanup: - svn_ra_neon__request_destroy(request); - SVN_ERR(err); - - if (pb->tmpfile) - { - /* We're done with the file. this should delete it. Note: it - isn't a big deal if this line is never executed -- the pool - will eventually get it. We're just being proactive here. */ - (void) apr_file_close(pb->tmpfile); - } - } - - /* 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(file, pool); -} - - -static svn_error_t * commit_close_edit(void *edit_baton, - apr_pool_t *pool) -{ - 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), - &(commit_info->author), - &(commit_info->post_commit_err), - cc->ras, - cc->ras->root.path, - merge_resource_url, - cc->valid_targets, - cc->lock_tokens, - cc->keep_locks, - cc->ras->callbacks->push_wc_prop == NULL, - 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)); - - return SVN_NO_ERROR; -} - - -static svn_error_t * commit_abort_edit(void *edit_baton, - apr_pool_t *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; -} - - -svn_error_t * svn_ra_neon__get_commit_editor(svn_ra_session_t *session, - const svn_delta_editor_t **editor, - void **edit_baton, - apr_hash_t *revprop_table, - svn_commit_callback2_t callback, - void *callback_baton, - apr_hash_t *lock_tokens, - svn_boolean_t keep_locks, - apr_pool_t *pool) -{ - svn_ra_neon__session_t *ras = session->priv; - svn_delta_editor_t *commit_editor; - commit_ctx_t *cc; - apr_hash_index_t *hi; - - /* Build the main commit editor's baton. */ - cc = apr_pcalloc(pool, sizeof(*cc)); - cc->pool = pool; - cc->ras = ras; - cc->valid_targets = apr_hash_make(pool); - cc->callback = callback; - cc->callback_baton = callback_baton; - cc->lock_tokens = lock_tokens; - cc->keep_locks = keep_locks; - - /* Dup the revprops into POOL, in case the caller clears the pool - they're in before driving the editor that this function returns. */ - cc->revprop_table = apr_hash_make(pool); - for (hi = apr_hash_first(pool, revprop_table); hi; hi = apr_hash_next(hi)) - { - const void *key; - apr_ssize_t klen; - void *val; - - apr_hash_this(hi, &key, &klen, &val); - apr_hash_set(cc->revprop_table, apr_pstrdup(pool, key), klen, - 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; - commit_editor->delete_entry = commit_delete_entry; - commit_editor->add_directory = commit_add_dir; - commit_editor->open_directory = commit_open_dir; - commit_editor->change_dir_prop = commit_change_dir_prop; - commit_editor->close_directory = commit_close_dir; - commit_editor->add_file = commit_add_file; - commit_editor->open_file = commit_open_file; - commit_editor->apply_textdelta = commit_apply_txdelta; - commit_editor->change_file_prop = commit_change_file_prop; - commit_editor->close_file = commit_close_file; - commit_editor->close_edit = commit_close_edit; - commit_editor->abort_edit = commit_abort_edit; - - *editor = commit_editor; - *edit_baton = cc; - return SVN_NO_ERROR; -} |