summaryrefslogtreecommitdiff
path: root/subversion/libsvn_ra/editor.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_ra/editor.c')
-rw-r--r--subversion/libsvn_ra/editor.c339
1 files changed, 339 insertions, 0 deletions
diff --git a/subversion/libsvn_ra/editor.c b/subversion/libsvn_ra/editor.c
new file mode 100644
index 0000000..ac969ca
--- /dev/null
+++ b/subversion/libsvn_ra/editor.c
@@ -0,0 +1,339 @@
+/*
+ * editor.c: compatibility editors
+ *
+ * ====================================================================
+ * 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 "svn_error.h"
+#include "svn_pools.h"
+#include "svn_ra.h"
+#include "svn_delta.h"
+#include "svn_dirent_uri.h"
+
+#include "private/svn_ra_private.h"
+#include "private/svn_delta_private.h"
+#include "private/svn_editor.h"
+
+#include "ra_loader.h"
+#include "svn_private_config.h"
+
+
+struct fp_baton {
+ svn_ra__provide_props_cb_t provide_props_cb;
+ void *cb_baton;
+};
+
+struct fb_baton {
+ svn_ra__provide_base_cb_t provide_base_cb;
+ void *cb_baton;
+};
+
+/* The shims currently want a callback that provides props for a given
+ REPOS_RELPATH at a given BASE_REVISION. However, the RA Ev2 interface
+ has a callback that provides properties for the REPOS_RELPATH from any
+ revision, which is returned along with the properties.
+
+ This is a little shim to map between the prototypes. The base revision
+ for the properties is discarded, and the requested revision (from the
+ shim code) is ignored.
+
+ The shim code needs to be updated to allow for an RA-style callback
+ to fetch properties. */
+static svn_error_t *
+fetch_props(apr_hash_t **props,
+ void *baton,
+ const char *repos_relpath,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct fp_baton *fpb = baton;
+ svn_revnum_t unused_revision;
+
+ /* Ignored: BASE_REVISION. */
+
+ return svn_error_trace(fpb->provide_props_cb(props, &unused_revision,
+ fpb->cb_baton,
+ repos_relpath,
+ result_pool, scratch_pool));
+}
+
+/* See note above regarding BASE_REVISION.
+ This also pulls down the entire contents of the file stream from the
+ RA layer and stores them in a local file, returning the path.
+*/
+static svn_error_t *
+fetch_base(const char **filename,
+ void *baton,
+ const char *repos_relpath,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct fb_baton *fbb = baton;
+ svn_revnum_t unused_revision;
+ svn_stream_t *contents;
+ svn_stream_t *file_stream;
+ const char *tmp_filename;
+
+ /* Ignored: BASE_REVISION. */
+
+ SVN_ERR(fbb->provide_base_cb(&contents, &unused_revision, fbb->cb_baton,
+ repos_relpath, result_pool, scratch_pool));
+
+ SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
+
+ *filename = apr_pstrdup(result_pool, tmp_filename);
+
+
+
+ return SVN_NO_ERROR;
+}
+
+
+
+svn_error_t *
+svn_ra__use_commit_shim(svn_editor_t **editor,
+ svn_ra_session_t *session,
+ apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ apr_hash_t *lock_tokens,
+ svn_boolean_t keep_locks,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
+ void *cb_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const svn_delta_editor_t *deditor;
+ void *dedit_baton;
+ struct svn_delta__extra_baton *exb;
+ svn_delta__unlock_func_t unlock_func;
+ void *unlock_baton;
+ const char *repos_root;
+ const char *session_url;
+ const char *base_relpath;
+ svn_boolean_t *found_abs_paths;
+ struct fp_baton *fpb;
+
+ /* NOTE: PROVIDE_BASE_CB is currently unused by this shim. In the future,
+ we can pass it to the underlying Ev2/Ev1 shim to produce better
+ apply_txdelta drives (ie. against a base rather than <empty>). */
+
+ /* Fetch the RA provider's Ev1 commit editor. */
+ SVN_ERR(session->vtable->get_commit_editor(session, &deditor, &dedit_baton,
+ revprop_table,
+ commit_callback, commit_baton,
+ lock_tokens, keep_locks,
+ result_pool));
+
+ /* Get or calculate the appropriate repos root and base relpath. */
+ SVN_ERR(svn_ra_get_repos_root2(session, &repos_root, scratch_pool));
+ SVN_ERR(svn_ra_get_session_url(session, &session_url, scratch_pool));
+ base_relpath = svn_uri_skip_ancestor(repos_root, session_url, scratch_pool);
+
+ /* We will assume that when the underlying Ev1 editor is finally driven
+ by the shim, that we will not need to prepend "/" to the paths. Place
+ this on the heap because it is examined much later. Set to FALSE. */
+ found_abs_paths = apr_pcalloc(result_pool, sizeof(*found_abs_paths));
+
+ /* The PROVIDE_PROPS_CB callback does not match what the shims want.
+ Let's jigger things around a little bit here. */
+ fpb = apr_palloc(result_pool, sizeof(*fpb));
+ fpb->provide_props_cb = provide_props_cb;
+ fpb->cb_baton = cb_baton;
+
+ /* Create the Ev2 editor from the Ev1 editor provided by the RA layer.
+
+ Note: GET_COPYSRC_KIND_CB is compatible in type/semantics with the
+ shim's FETCH_KIND_FUNC parameter. */
+ SVN_ERR(svn_delta__editor_from_delta(editor, &exb,
+ &unlock_func, &unlock_baton,
+ deditor, dedit_baton,
+ found_abs_paths,
+ repos_root, base_relpath,
+ cancel_func, cancel_baton,
+ get_copysrc_kind_cb, cb_baton,
+ fetch_props, fpb,
+ result_pool, scratch_pool));
+
+ /* Note: UNLOCK_FUNC and UNLOCK_BATON are unused during commit drives.
+ We can safely drop them on the floor. */
+
+ /* Since we're (currently) just wrapping an existing Ev1 editor, we have
+ to call any start_edit handler it may provide (the shim uses this to
+ invoke Ev1's open_root callback). We've got a couple of options to do
+ so: Implement a wrapper editor and call the start_edit callback upon
+ the first invocation of any of the underlying editor's functions; or,
+ just assume our consumer is going to eventually use the editor it is
+ asking for, and call the start edit callback now. For simplicity's
+ sake, we do the latter. */
+ if (exb->start_edit)
+ {
+ /* Most commit drives pass SVN_INVALID_REVNUM for the revision.
+ All calls to svn_delta_path_driver() pass SVN_INVALID_REVNUM,
+ so this is fine for any commits done via that function.
+
+ Notably, the PROPSET command passes a specific revision. Before
+ PROPSET can use the RA Ev2 interface, we may need to make this
+ revision a parameter.
+ ### what are the exact semantics? what is the meaning of the
+ ### revision passed to the Ev1->open_root() callback? */
+ SVN_ERR(exb->start_edit(exb->baton, SVN_INVALID_REVNUM));
+ }
+
+ /* Note: EXB also contains a TARGET_REVISION function, but that is not
+ used during commit operations. We can safely ignore it. (ie. it is
+ in EXB for use by paired-shims) */
+
+ return SVN_NO_ERROR;
+}
+
+
+struct wrapped_replay_baton_t {
+ svn_ra__replay_revstart_ev2_callback_t revstart_func;
+ svn_ra__replay_revfinish_ev2_callback_t revfinish_func;
+ void *replay_baton;
+
+ svn_ra_session_t *session;
+
+ svn_ra__provide_base_cb_t provide_base_cb;
+ svn_ra__provide_props_cb_t provide_props_cb;
+ void *cb_baton;
+
+ /* This will be populated by the revstart wrapper. */
+ svn_editor_t *editor;
+};
+
+static svn_error_t *
+revstart_func_wrapper(svn_revnum_t revision,
+ void *replay_baton,
+ const svn_delta_editor_t **deditor,
+ void **dedit_baton,
+ apr_hash_t *rev_props,
+ apr_pool_t *result_pool)
+{
+ struct wrapped_replay_baton_t *wrb = replay_baton;
+ const char *repos_root;
+ const char *session_url;
+ const char *base_relpath;
+ svn_boolean_t *found_abs_paths;
+ struct fp_baton *fpb;
+ struct svn_delta__extra_baton *exb;
+
+ /* Get the Ev2 editor from the original revstart func. */
+ SVN_ERR(wrb->revstart_func(revision, wrb->replay_baton, &wrb->editor,
+ rev_props, result_pool));
+
+ /* Get or calculate the appropriate repos root and base relpath. */
+ SVN_ERR(svn_ra_get_repos_root2(wrb->session, &repos_root, result_pool));
+ SVN_ERR(svn_ra_get_session_url(wrb->session, &session_url, result_pool));
+ base_relpath = svn_uri_skip_ancestor(repos_root, session_url, result_pool);
+
+ /* We will assume that when the underlying Ev1 editor is finally driven
+ by the shim, that we will not need to prepend "/" to the paths. Place
+ this on the heap because it is examined much later. Set to FALSE. */
+ found_abs_paths = apr_pcalloc(result_pool, sizeof(*found_abs_paths));
+
+ /* The PROVIDE_PROPS_CB callback does not match what the shims want.
+ Let's jigger things around a little bit here. */
+ fpb = apr_palloc(result_pool, sizeof(*fpb));
+ fpb->provide_props_cb = wrb->provide_props_cb;
+ fpb->cb_baton = wrb->cb_baton;
+
+ /* Create the extra baton. */
+ exb = apr_pcalloc(result_pool, sizeof(*exb));
+
+ /* Create the Ev1 editor from the Ev2 editor provided by the RA layer.
+
+ Note: GET_COPYSRC_KIND_CB is compatible in type/semantics with the
+ shim's FETCH_KIND_FUNC parameter. */
+ SVN_ERR(svn_delta__delta_from_editor(deditor, dedit_baton, wrb->editor,
+ NULL, NULL,
+ found_abs_paths,
+ repos_root, base_relpath,
+ fetch_props, wrb->cb_baton,
+ fetch_base, wrb->cb_baton,
+ exb, result_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+revfinish_func_wrapper(svn_revnum_t revision,
+ void *replay_baton,
+ const svn_delta_editor_t *editor,
+ void *edit_baton,
+ apr_hash_t *rev_props,
+ apr_pool_t *pool)
+{
+ struct wrapped_replay_baton_t *wrb = replay_baton;
+
+ SVN_ERR(wrb->revfinish_func(revision, replay_baton, wrb->editor, rev_props,
+ pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_ra__use_replay_range_shim(svn_ra_session_t *session,
+ svn_revnum_t start_revision,
+ svn_revnum_t end_revision,
+ svn_revnum_t low_water_mark,
+ svn_boolean_t send_deltas,
+ svn_ra__replay_revstart_ev2_callback_t revstart_func,
+ svn_ra__replay_revfinish_ev2_callback_t revfinish_func,
+ void *replay_baton,
+ svn_ra__provide_base_cb_t provide_base_cb,
+ svn_ra__provide_props_cb_t provide_props_cb,
+ void *cb_baton,
+ apr_pool_t *scratch_pool)
+{
+ /* The basic strategy here is to wrap the callback start and finish
+ functions to appropriately return an Ev1 editor which is itself wrapped
+ from the Ev2 one the provided callbacks will give us. */
+
+ struct wrapped_replay_baton_t *wrb = apr_pcalloc(scratch_pool, sizeof(*wrb));
+
+ wrb->revstart_func = revstart_func;
+ wrb->revfinish_func = revfinish_func;
+ wrb->replay_baton = replay_baton;
+ wrb->session = session;
+
+ wrb->provide_base_cb = provide_base_cb;
+ wrb->provide_props_cb = provide_props_cb;
+ wrb->cb_baton = cb_baton;
+
+ return svn_error_trace(svn_ra_replay_range(session, start_revision,
+ end_revision, low_water_mark,
+ send_deltas,
+ revstart_func_wrapper,
+ revfinish_func_wrapper,
+ wrb, scratch_pool));
+}