Author: rhuijben
Date: Fri Dec 13 16:14:06 2013
New Revision: 1550758
URL: http://svn.apache.org/r1550758
Log:
Checkpoint a new libsvn_client api that when tested can replace most of the
svnmucc code, while re-using some of the commit infrastructure in
libsvn_client.
Most of the code is implemented now, so in theory it should work, but to
prove or disprove that the next step will be writing some testcases :)
* subversion/include/svn_client.h
(svn_client_mtcc_t): New typedef.
(svn_client_mtcc_create
svn_client_mtcc_get_relpath
svn_client_mtcc_add_add_file
svn_client_mtcc_add_copy
svn_client_mtcc_add_delete
svn_client_mtcc_add_mkdir
svn_client_mtcc_add_move
svn_client_mtcc_add_propset
svn_client_mtcc_add_update_file
svn_client_mtcc_commit): New functions.
* subversion/libsvn_client/mtcc.c
New file.
* subversion/libsvn_client/mtcc.h
New file.
Added:
subversion/trunk/subversion/libsvn_client/mtcc.c (with props)
subversion/trunk/subversion/libsvn_client/mtcc.h (with props)
Modified:
subversion/trunk/subversion/include/svn_client.h
Modified: subversion/trunk/subversion/include/svn_client.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1550758&r1=1550757&r2=1550758&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Fri Dec 13 16:14:06 2013
@@ -6653,6 +6653,183 @@ svn_client_open_ra_session(svn_ra_sessio
/** @} end group: Client session related functions */
+/**
+ *
+ * @defgroup clnt_mtcc Multi Command Context related functions
+ *
+ * @{
+ *
+ */
+
+/** This is a structure which stores a list of repository commands
+ * that can be played to a repository as a single operation
+ *
+ * Use svn_client_mtcc_create() to create instances
+ *
+ * @since New in 1.9.
+ */
+typedef struct svn_client_mtcc_t svn_client_mtcc_t;
+
+/** Creates a new multicommand context for an operation on @a anchor_url and
+ * its descendants.
+ *
+ * Allocate the context in @a result_pool and perform temporary allocations in
+ * @a scratch_pool.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_create(svn_client_mtcc_t **mtcc,
+ const char *anchor_url,
+ svn_revnum_t base_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Calculates a relative path @a *relpath for @a url, reparenting the
+ * @a mtcc to a higher anchor url if necessary.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_get_relpath(const char **relpath,
+ const char *url,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/** Adds a file add operation of @a relpath to @a mtcc. If @a src_checksum
+ * is not null it will be provided to the repository to verify if the file
+ * was transfered succesfull.
+ *
+ * Perform temporary allocations in @a scratch_pool.
+ *
+ * @note The current implementation keeps @a src_stream open until @a mtcc
+ * is committed.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_add_add_file(const char *relpath,
+ svn_stream_t *src_stream,
+ const svn_checksum_t *src_checksum,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool);
+
+/** Adds a copy operation of the node @a src_relpath at revision @a revision
+ * to @a dst_relpath to @a mtcc.
+ *
+ * Perform temporary allocations in @a scratch_pool.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_add_copy(const char *src_relpath,
+ svn_revnum_t revision,
+ const char *dst_relpath,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool);
+
+/** Adds a delete of @a relpath to @a mtcc.
+ *
+ * Perform temporary allocations in @a scratch_pool.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_add_delete(const char *relpath,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool);
+
+/** Adds an mkdir operation of @a relpath to @a mtcc.
+ *
+ * Perform temporary allocations in @a scratch_pool.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_add_mkdir(const char *relpath,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool);
+
+
+/** Adds a move operation of the node @a src_relpath to @a dst_relpath to
+ * @a mtcc.
+ *
+ * Perform temporary allocations in @a scratch_pool.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_add_move(const char *src_relpath,
+ const char *dst_relpath,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool);
+
+/** Adds a propset operation for the property @a propname to @a propval
+ * (which can be NULL for a delete) on @a relpath to @a mtcc.
+ *
+ * If @a skip_checks is not FALSE Subversion defined properties are verified
+ * for correctness like svn_client_propset_remote()
+ *
+ * Perform temporary allocations in @a scratch_pool.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_add_propset(const char *relpath,
+ const char *propname,
+ const svn_string_t *propval,
+ svn_boolean_t skip_checks,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool);
+
+
+/** Adds an update file operation for @a relpath to @a mtcc.
+ *
+ * The final version of the file is provided with @a src_stream. If @a
+ * src_checksum is provided it will be provided to the repository to verify
+ * the final result.
+ *
+ * If @a base_checksum is provided it will be used by the repository to verify
+ * if the base file matches this checksum.
+ *
+ * If @a base_stream is not NULL only the binary diff from @a base_stream to
+ * @a src_stream is written to the repository.
+ *
+ * Perform temporary allocations in @a scratch_pool.
+ *
+ * @note Callers should assume that the mtcc requires @a src_stream and @a
+ * base_stream to be valid until @a mtcc is committed.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_add_update_file(const char *relpath,
+ svn_stream_t *src_stream,
+ const svn_checksum_t *src_checksum,
+ svn_stream_t *base_stream,
+ const svn_checksum_t *base_checksum,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool);
+
+/** Commits all operations stored in @a mtcc as a new revision and destroys
+ * @a mtcc.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_mtcc_commit(apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool);
+
+
+/** @} end group: Multi Command Context related functions */
+
+
+
/** @} */
#ifdef __cplusplus
Added: subversion/trunk/subversion/libsvn_client/mtcc.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/mtcc.c?rev=1550758&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_client/mtcc.c (added)
+++ subversion/trunk/subversion/libsvn_client/mtcc.c Fri Dec 13 16:14:06 2013
@@ -0,0 +1,699 @@
+/*
+ * mtcc.c -- Multi Command Context implementation. This allows
+ * performing many operations without a working copy.
+ *
+ * ====================================================================
+ * 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 "svn_dirent_uri.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_props.h"
+#include "svn_pools.h"
+#include "svn_subst.h"
+
+#include "svn_private_config.h"
+
+#include "client.h"
+#include "mtcc.h"
+
+#include <assert.h>
+
+static svn_client_mtcc_op_t *
+mtcc_op_create(const char *name,
+ svn_boolean_t add,
+ svn_boolean_t directory,
+ apr_pool_t *result_pool)
+{
+ svn_client_mtcc_op_t *op;
+
+ op = apr_pcalloc(result_pool, sizeof(*op));
+ op->name = name ? apr_pstrdup(result_pool, name) : "";
+
+ if (add)
+ op->kind = directory ? OP_ADD_DIR : OP_ADD_FILE;
+ else
+ op->kind = directory ? OP_OPEN_DIR : OP_OPEN_FILE;
+
+ if (directory)
+ op->children = apr_array_make(result_pool, 4,
+ sizeof(svn_client_mtcc_op_t *));
+
+ return op;
+}
+
+static svn_error_t *
+mtcc_op_find(svn_client_mtcc_op_t **op,
+ svn_boolean_t *created,
+ const char *relpath,
+ svn_client_mtcc_op_t *base_op,
+ svn_boolean_t find_existing,
+ svn_boolean_t find_deletes,
+ svn_boolean_t create_file,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *name;
+ const char *child;
+ int i;
+
+ assert(svn_relpath_is_canonical(relpath));
+ *created = FALSE;
+
+ if (!*relpath)
+ {
+ if (find_existing)
+ *op = base_op;
+ else
+ *op = NULL;
+
+ return SVN_NO_ERROR;
+ }
+
+ child = strchr(relpath, '/');
+
+ if (child)
+ {
+ name = apr_pstrmemdup(scratch_pool, relpath, (relpath-child));
+ child++; /* Skip '/' */
+ }
+ else
+ name = relpath;
+
+ if (child && !base_op->children)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't operate on '%s' because '%s' is not a "
+ "directory"),
+ child, base_op->name);
+
+ for (i = 0; i < base_op->children->nelts; i++)
+ {
+ svn_client_mtcc_op_t *cop;
+
+ cop = APR_ARRAY_IDX(base_op->children, i, svn_client_mtcc_op_t *);
+
+ if (! strcmp(cop->name, name)
+ && (find_deletes || cop->kind != OP_DELETE))
+ {
+ return svn_error_trace(
+ mtcc_op_find(op, created, child ? child : "", cop,
+ find_existing, find_deletes, create_file,
+ result_pool, scratch_pool));
+ }
+ }
+
+ if (!created)
+ {
+ *op = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ {
+ svn_client_mtcc_op_t *cop;
+
+ cop = mtcc_op_create(name, FALSE, child || !create_file, result_pool);
+
+ APR_ARRAY_PUSH(base_op->children, svn_client_mtcc_op_t *) = cop;
+
+ if (!child)
+ {
+ *created = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(
+ mtcc_op_find(op, created, child, cop, find_existing,
+ find_deletes, create_file,
+ result_pool, scratch_pool));
+ }
+}
+
+svn_error_t *
+svn_client_mtcc_create(svn_client_mtcc_t **mtcc,
+ const char *anchor_url,
+ svn_revnum_t base_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *mtcc_pool;
+
+ mtcc_pool = svn_pool_create(result_pool);
+
+ *mtcc = apr_pcalloc(mtcc_pool, sizeof(*mtcc));
+ (*mtcc)->pool = mtcc_pool;
+ (*mtcc)->base_revision = base_revision;
+
+ (*mtcc)->root_op = mtcc_op_create(NULL, FALSE, TRUE, mtcc_pool);
+
+ (*mtcc)->ctx = ctx;
+
+ SVN_ERR(svn_client_open_ra_session2(&(*mtcc)->ra_session, anchor_url,
+ NULL /* wri_abspath */, ctx,
+ mtcc_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+update_copy_src(svn_client_mtcc_op_t *op,
+ const char *add_relpath,
+ apr_pool_t *result_pool)
+{
+ int i;
+
+ if (op->src_relpath)
+ op->src_relpath = svn_relpath_join(add_relpath, op->src_relpath,
+ result_pool);
+
+ if (!op->children)
+ return SVN_NO_ERROR;
+
+ for (i = 0; i < op->children->nelts; i++)
+ {
+ svn_client_mtcc_op_t *cop;
+
+ cop = APR_ARRAY_IDX(op->children, i, svn_client_mtcc_op_t *);
+
+ SVN_ERR(update_copy_src(cop, add_relpath, result_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_mtcc_get_relpath(const char **relpath,
+ const char *url,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ const char *new_anchor;
+ const char *session_url;
+ const char *up;
+
+ err = svn_ra_get_path_relative_to_session(mtcc->ra_session, relpath, url,
+ result_pool);
+
+ if (! err || err->apr_err != SVN_ERR_RA_ILLEGAL_URL)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+
+ SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url,
+ scratch_pool));
+
+ new_anchor = svn_uri_get_longest_ancestor(url, session_url, scratch_pool);
+
+ if (svn_path_is_empty(new_anchor))
+ {
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("'%s' is not in the same repository as '%s'"),
+ url, session_url);
+ }
+
+ up = svn_uri_skip_ancestor(new_anchor, session_url, scratch_pool);
+
+ /* Update copy origins recursively...:( */
+ SVN_ERR(update_copy_src(mtcc->root_op, up, mtcc->pool));
+
+ SVN_ERR(svn_ra_reparent(mtcc->ra_session, new_anchor, scratch_pool));
+
+ /* Create directory open operations for new ancestors */
+ while (*up)
+ {
+ svn_client_mtcc_op_t *root_op;
+
+ mtcc->root_op->name = svn_relpath_basename(up, mtcc->pool);
+ up = svn_relpath_dirname(up, scratch_pool);
+
+ root_op = mtcc_op_create(NULL, FALSE, TRUE, mtcc->pool);
+
+ APR_ARRAY_PUSH(root_op->children,
+ svn_client_mtcc_op_t *) = mtcc->root_op;
+
+ mtcc->root_op = root_op;
+ }
+
+ return svn_error_trace(
+ svn_ra_get_path_relative_to_session(mtcc->ra_session, relpath, url,
+ result_pool));
+}
+
+
+svn_error_t *
+svn_client_mtcc_add_add_file(const char *relpath,
+ svn_stream_t *src_stream,
+ const svn_checksum_t *src_checksum,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_mtcc_op_t *op;
+ svn_boolean_t created;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream);
+
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE,
+ TRUE, mtcc->pool, scratch_pool));
+
+ if (!op || !created)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't add file at '%s'"),
+ relpath);
+ }
+
+ op->kind = OP_ADD_FILE;
+ op->src_stream = src_stream;
+ op->src_checksum = src_checksum ? svn_checksum_dup(src_checksum, mtcc->pool)
+ : NULL;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_mtcc_add_copy(const char *src_relpath,
+ svn_revnum_t revision,
+ const char *dst_relpath,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_mtcc_op_t *op;
+ svn_boolean_t created;
+ svn_node_kind_t kind;
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath)
+ && svn_relpath_is_canonical(dst_relpath)
+ && SVN_IS_VALID_REVNUM(revision));
+
+ /* Subversion requires the kind of a copy */
+ SVN_ERR(svn_ra_check_path(mtcc->ra_session, src_relpath, revision, &kind,
+ scratch_pool));
+
+ if (kind != svn_node_dir && kind != svn_node_file)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't create a copy of '%s' at revision %ld "
+ "as it does not exist"),
+ src_relpath, revision);
+ }
+
+ SVN_ERR(mtcc_op_find(&op, &created, dst_relpath, mtcc->root_op, FALSE, FALSE,
+ (kind == svn_node_file), mtcc->pool, scratch_pool));
+
+ if (!op || !created)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't add node at '%s'"),
+ dst_relpath);
+ }
+
+ op->kind = (kind == svn_node_file) ? OP_ADD_FILE : OP_ADD_DIR;
+ op->src_relpath = apr_pstrdup(mtcc->pool, src_relpath);
+ op->src_rev = revision;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_mtcc_add_delete(const char *relpath,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_mtcc_op_t *op;
+ svn_boolean_t created;
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, TRUE,
+ TRUE, mtcc->pool, scratch_pool));
+
+ if (!op || !created)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't delete node at '%s'"),
+ relpath);
+ }
+
+ op->kind = OP_DELETE;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_mtcc_add_mkdir(const char *relpath,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_mtcc_op_t *op;
+ svn_boolean_t created;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE,
+ FALSE, mtcc->pool, scratch_pool));
+
+ if (!op || !created)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't create directory at '%s'"),
+ relpath);
+ }
+
+ op->kind = OP_ADD_DIR;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_mtcc_add_move(const char *src_relpath,
+ const char *dst_relpath,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(svn_client_mtcc_add_delete(src_relpath, mtcc, scratch_pool));
+ SVN_ERR(svn_client_mtcc_add_copy(src_relpath, mtcc->base_revision,
+ dst_relpath, mtcc, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_mtcc_add_propset(const char *relpath,
+ const char *propname,
+ const svn_string_t *propval,
+ svn_boolean_t skip_checks,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_mtcc_op_t *op;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)
+ && svn_property_kind2(propname) == svn_prop_regular_kind);
+
+ if (!skip_checks && svn_prop_needs_translation(propname))
+ {
+ svn_string_t *translated_value;
+ SVN_ERR_W(svn_subst_translate_string2(&translated_value, NULL,
+ NULL, propval,
+ NULL, FALSE,
+ scratch_pool, scratch_pool),
+ _("Error normalizing property value"));
+ }
+
+ SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE,
+ FALSE, mtcc->pool, scratch_pool));
+
+ if (!op)
+ {
+ svn_node_kind_t kind;
+ svn_boolean_t created;
+ const char *origin_relpath = relpath;
+ svn_revnum_t origin_revnum = mtcc->base_revision;
+
+ /* ### TODO: Check if this node is within a newly copied directory,
+ and update origin values accordingly */
+
+ SVN_ERR(svn_ra_check_path(mtcc->ra_session, origin_relpath,
+ origin_revnum, &kind, scratch_pool));
+
+ if (kind != svn_node_file && kind != svn_node_dir)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't set properties at not existing
'%s'"),
+ relpath);
+
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE,
+ (kind != svn_node_dir),
+ mtcc->pool, scratch_pool));
+
+ SVN_ERR_ASSERT(op != NULL);
+ }
+
+ if (!op->prop_mods)
+ op->prop_mods = apr_array_make(mtcc->pool, 4, sizeof(svn_prop_t));
+
+ {
+ svn_prop_t propchange;
+ propchange.name = apr_pstrdup(mtcc->pool, propname);
+
+ if (propval)
+ propchange.value = svn_string_dup(propval, mtcc->pool);
+ else
+ propchange.value = NULL;
+
+ APR_ARRAY_PUSH(op->prop_mods, svn_prop_t) = propchange;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_mtcc_add_update_file(const char *relpath,
+ svn_stream_t *src_stream,
+ const svn_checksum_t *src_checksum,
+ svn_stream_t *base_stream,
+ const svn_checksum_t *base_checksum,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_mtcc_op_t *op;
+ svn_boolean_t created;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream);
+
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE,
+ TRUE, mtcc->pool, scratch_pool));
+
+ if (!op
+ || (op->kind != OP_OPEN_FILE && op->kind != OP_ADD_FILE)
+ || (op->src_stream != NULL))
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't update file at '%s'"), relpath);
+ }
+
+ op->src_stream = src_stream;
+ op->src_checksum = src_checksum ? svn_checksum_dup(src_checksum, mtcc->pool)
+ : NULL;
+
+ op->base_stream = base_stream;
+ op->base_checksum = base_checksum ? svn_checksum_dup(base_checksum,
+ mtcc->pool)
+ : NULL;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+commit_properties(const svn_delta_editor_t *editor,
+ const svn_client_mtcc_op_t *op,
+ void *node_baton,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ apr_pool_t *iterpool;
+
+ if (!op->prop_mods || op->prop_mods->nelts == 0)
+ return SVN_NO_ERROR;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < op->prop_mods->nelts; i++)
+ {
+ const svn_prop_t *mod = &APR_ARRAY_IDX(op->prop_mods, i, svn_prop_t);
+
+ svn_pool_clear(iterpool);
+
+ if (op->kind == OP_ADD_DIR || op->kind == OP_OPEN_DIR)
+ SVN_ERR(editor->change_dir_prop(node_baton, mod->name, mod->value,
+ iterpool));
+ else if (op->kind == OP_ADD_FILE || op->kind == OP_OPEN_FILE)
+ SVN_ERR(editor->change_file_prop(node_baton, mod->name, mod->value,
+ iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+commit_file(const svn_delta_editor_t *editor,
+ svn_client_mtcc_op_t *op,
+ void *file_baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *text_checksum = NULL;
+ svn_checksum_t *src_checksum = op->src_checksum;
+ SVN_ERR(commit_properties(editor, op, file_baton, scratch_pool));
+
+ if (op->src_stream)
+ {
+ const char *base_checksum = NULL;
+ apr_pool_t *txdelta_pool = scratch_pool;
+ svn_txdelta_window_handler_t window_handler;
+ void *handler_baton;
+ svn_stream_t *src_stream = op->src_stream;
+
+ if (op->base_checksum && op->base_checksum->kind == svn_checksum_md5)
+ base_checksum = svn_checksum_to_cstring(op->base_checksum,
scratch_pool);
+
+ /* ### TODO: Future enhancement: Allocate in special pool and send
+ files after the true edit operation, like a wc commit */
+ SVN_ERR(editor->apply_textdelta(file_baton, base_checksum, txdelta_pool,
+ &window_handler, &handler_baton));
+
+ if (window_handler != svn_delta_noop_window_handler)
+ {
+ if (!src_checksum || src_checksum->kind != svn_checksum_md5)
+ src_stream = svn_stream_checksummed2(src_stream, NULL,
&src_checksum,
+ svn_checksum_md5,
+ TRUE, scratch_pool);
+
+ if (!op->base_stream)
+ SVN_ERR(svn_txdelta_send_stream(src_stream,
+ window_handler, handler_baton,
NULL,
+ scratch_pool));
+ else
+ SVN_ERR(svn_txdelta_run(op->base_stream, src_stream,
+ window_handler, handler_baton,
+ svn_checksum_md5, NULL,
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool, scratch_pool));
+ }
+ else
+ {
+ if (op->src_stream)
+ SVN_ERR(svn_stream_close(op->src_stream));
+ if (op->base_stream)
+ SVN_ERR(svn_stream_close(op->base_stream));
+ }
+ }
+
+ if (op->src_checksum && op->src_checksum->kind == svn_checksum_md5)
+ text_checksum = svn_checksum_to_cstring(op->src_checksum, scratch_pool);
+
+ return svn_error_trace(editor->close_file(file_baton, text_checksum,
+ scratch_pool));
+}
+
+static svn_error_t *
+commit_directory(svn_delta_editor_t *editor,
+ svn_client_mtcc_op_t *op,
+ const char *relpath,
+ svn_revnum_t base_rev,
+ void *dir_baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(commit_properties(editor, op, dir_baton, scratch_pool));
+
+ if (op->children && op->children->nelts > 0)
+ {
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ int i;
+
+ for (i = 0; i < op->children->nelts; i++)
+ {
+ svn_client_mtcc_op_t *cop;
+ const char * child_relpath;
+ void *child_baton;
+
+ cop = APR_ARRAY_IDX(op->children, i, svn_client_mtcc_op_t *);
+
+ svn_pool_clear(iterpool);
+
+ child_relpath = svn_relpath_join(relpath, cop->name, iterpool);
+
+ switch (cop->kind)
+ {
+ case OP_DELETE:
+ SVN_ERR(editor->delete_entry(child_relpath, base_rev,
+ dir_baton, iterpool));
+ break;
+
+ case OP_ADD_DIR:
+ SVN_ERR(editor->add_directory(child_relpath, dir_baton,
+ cop->src_relpath, cop->src_rev,
+ iterpool, &child_baton));
+ SVN_ERR(commit_directory(editor, cop, child_relpath,
+ SVN_INVALID_REVNUM, child_baton, ctx,
+ iterpool));
+ break;
+ case OP_OPEN_DIR:
+ SVN_ERR(editor->open_directory(child_relpath, dir_baton,
+ base_rev, iterpool,
&child_baton));
+ SVN_ERR(commit_directory(editor, cop, child_relpath,
+ SVN_INVALID_REVNUM, child_baton, ctx,
+ iterpool));
+ break;
+
+ case OP_ADD_FILE:
+ SVN_ERR(editor->add_file(child_relpath, dir_baton,
+ cop->src_relpath, cop->src_rev,
+ iterpool, &child_baton));
+ SVN_ERR(commit_file(editor, cop, child_baton, ctx, iterpool));
+ break;
+ case OP_OPEN_FILE:
+ SVN_ERR(editor->open_file(child_relpath, dir_baton, base_rev,
+ iterpool, &child_baton));
+ SVN_ERR(commit_file(editor, cop, child_baton, ctx, iterpool));
+ break;
+
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+ }
+ }
+
+ return svn_error_trace(editor->close_directory(dir_baton, scratch_pool));
+}
+
+svn_error_t *
+svn_client_mtcc_commit(apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ svn_client_mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ svn_delta_editor_t *editor;
+ void *edit_baton;
+ svn_error_t *err;
+ void *root_baton;
+
+ SVN_ERR(svn_ra_get_commit_editor3(mtcc->ra_session, &editor, &edit_baton,
+ revprop_table,
+ commit_callback, commit_baton,
+ NULL /* lock_tokens */,
+ FALSE /* keep_locks */,
+ scratch_pool));
+
+ err = editor->open_root(edit_baton, mtcc->base_revision, scratch_pool,
&root_baton);
+
+ if (!err)
+ err = commit_directory(editor, mtcc->root_op, "", mtcc->base_revision,
+ root_baton, mtcc->ctx, scratch_pool);
+
+ if (!err)
+ SVN_ERR(editor->close_edit(edit_baton, scratch_pool));
+ else
+ err = svn_error_compose_create(err,
+ editor->abort_edit(edit_baton,
scratch_pool));
+
+ svn_pool_destroy(mtcc->pool);
+
+ return svn_error_trace(err);
+}
Propchange: subversion/trunk/subversion/libsvn_client/mtcc.c
------------------------------------------------------------------------------
svn:eol-style = native
Added: subversion/trunk/subversion/libsvn_client/mtcc.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/mtcc.h?rev=1550758&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_client/mtcc.h (added)
+++ subversion/trunk/subversion/libsvn_client/mtcc.h Fri Dec 13 16:14:06 2013
@@ -0,0 +1,67 @@
+/*
+ * mtcc.h : Multi Command Context definitions
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+#ifndef SVN_LIBSVN_CLIENT_MTCC_H
+#define SVN_LIBSVN_CLIENT_H
+
+/* The kind of operation to perform in an svn_client_mtcc_op_t */
+typedef enum svn_client_mtcc_kind_t
+{
+ OP_OPEN_DIR,
+ OP_OPEN_FILE,
+ OP_ADD_DIR,
+ OP_ADD_FILE,
+ OP_DELETE,
+} svn_client_mtcc_kind_t;
+
+typedef struct svn_client_mtcc_op_t
+{
+ const char *name; /* basename of operation */
+ svn_client_mtcc_kind_t kind; /* editor operation */
+
+ apr_array_header_t *children; /* List of svn_client_mtcc_op_t * */
+
+ const char *src_relpath; /* For ADD_DIR, ADD_FILE */
+ svn_revnum_t src_rev; /* For ADD_DIR, ADD_FILE */
+ svn_stream_t *src_stream; /* For ADD_FILE, OPEN_FILE */
+ svn_checksum_t *src_checksum; /* For ADD_FILE, OPEN_FILE */
+ svn_stream_t *base_stream; /* For ADD_FILE, OPEN_FILE */
+ const svn_checksum_t *base_checksum; /* For ADD_FILE, OPEN_FILE */
+
+ apr_array_header_t *prop_mods; /* For all except DELETE
+ List of svn_prop_t */
+} svn_client_mtcc_op_t;
+
+
+struct svn_client_mtcc_t
+{
+ apr_pool_t *pool;
+ svn_revnum_t base_revision;
+
+ svn_ra_session_t *ra_session;
+ svn_client_ctx_t *ctx;
+
+ svn_client_mtcc_op_t *root_op;
+};
+
+#endif
\ No newline at end of file
Propchange: subversion/trunk/subversion/libsvn_client/mtcc.h
------------------------------------------------------------------------------
svn:eol-style = native