summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/ra.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/ra.c')
-rw-r--r--subversion/libsvn_client/ra.c796
1 files changed, 564 insertions, 232 deletions
diff --git a/subversion/libsvn_client/ra.c b/subversion/libsvn_client/ra.c
index 248876f..a0d4cea 100644
--- a/subversion/libsvn_client/ra.c
+++ b/subversion/libsvn_client/ra.c
@@ -26,6 +26,7 @@
#include <apr_pools.h>
#include "svn_error.h"
+#include "svn_hash.h"
#include "svn_pools.h"
#include "svn_string.h"
#include "svn_sorts.h"
@@ -40,6 +41,7 @@
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
+#include "private/svn_client_private.h"
/* This is the baton that we pass svn_ra_open3(), and is associated with
@@ -51,9 +53,15 @@ typedef struct callback_baton_t
this base directory. */
const char *base_dir_abspath;
- /* When true, makes sure temporary files are created
- outside the working copy. */
- svn_boolean_t read_only_wc;
+ /* TEMPORARY: Is 'base_dir_abspath' a versioned path? cmpilato
+ suspects that the commit-to-multiple-disjoint-working-copies
+ code is getting this all wrong, sometimes passing an unversioned
+ (or versioned in a foreign wc) path here which sorta kinda
+ happens to work most of the time but is ultimately incorrect. */
+ svn_boolean_t base_dir_isversioned;
+
+ /* Used as wri_abspath for obtaining access to the pristine store */
+ const char *wcroot_abspath;
/* An array of svn_client_commit_item3_t * structures, present only
during working copy commits. */
@@ -62,9 +70,6 @@ typedef struct callback_baton_t
/* A client context. */
svn_client_ctx_t *ctx;
- /* The pool to use for session-related items. */
- apr_pool_t *pool;
-
} callback_baton_t;
@@ -153,7 +158,7 @@ push_wc_prop(void *baton,
if (! cb->commit_items)
return svn_error_createf
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Attempt to set wc property '%s' on '%s' in a non-commit operation"),
+ _("Attempt to set wcprop '%s' on '%s' in a non-commit operation"),
name, svn_dirent_local_style(relpath, pool));
for (i = 0; i < cb->commit_items->nelts; i++)
@@ -240,6 +245,30 @@ invalidate_wc_props(void *baton,
}
+/* This implements the `svn_ra_get_wc_contents_func_t' interface. */
+static svn_error_t *
+get_wc_contents(void *baton,
+ svn_stream_t **contents,
+ const svn_checksum_t *checksum,
+ apr_pool_t *pool)
+{
+ callback_baton_t *cb = baton;
+
+ if (! cb->wcroot_abspath)
+ {
+ *contents = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(
+ svn_wc__get_pristine_contents_by_checksum(contents,
+ cb->ctx->wc_ctx,
+ cb->wcroot_abspath,
+ checksum,
+ pool, pool));
+}
+
+
static svn_error_t *
cancel_callback(void *baton)
{
@@ -267,41 +296,47 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
const char *base_url,
const char *base_dir_abspath,
const apr_array_header_t *commit_items,
- svn_boolean_t use_admin,
- svn_boolean_t read_only_wc,
+ svn_boolean_t write_dav_props,
+ svn_boolean_t read_dav_props,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- svn_ra_callbacks2_t *cbtable = apr_pcalloc(pool, sizeof(*cbtable));
- callback_baton_t *cb = apr_pcalloc(pool, sizeof(*cb));
+ svn_ra_callbacks2_t *cbtable;
+ callback_baton_t *cb = apr_pcalloc(result_pool, sizeof(*cb));
const char *uuid = NULL;
- SVN_ERR_ASSERT(base_dir_abspath != NULL || ! use_admin);
+ SVN_ERR_ASSERT(!write_dav_props || read_dav_props);
+ SVN_ERR_ASSERT(!read_dav_props || base_dir_abspath != NULL);
SVN_ERR_ASSERT(base_dir_abspath == NULL
|| svn_dirent_is_absolute(base_dir_abspath));
+ SVN_ERR(svn_ra_create_callbacks(&cbtable, result_pool));
cbtable->open_tmp_file = open_tmp_file;
- cbtable->get_wc_prop = use_admin ? get_wc_prop : NULL;
- cbtable->set_wc_prop = read_only_wc ? NULL : set_wc_prop;
+ cbtable->get_wc_prop = read_dav_props ? get_wc_prop : NULL;
+ cbtable->set_wc_prop = (write_dav_props && read_dav_props)
+ ? set_wc_prop : NULL;
cbtable->push_wc_prop = commit_items ? push_wc_prop : NULL;
- cbtable->invalidate_wc_props = read_only_wc ? NULL : invalidate_wc_props;
+ cbtable->invalidate_wc_props = (write_dav_props && read_dav_props)
+ ? invalidate_wc_props : NULL;
cbtable->auth_baton = ctx->auth_baton; /* new-style */
cbtable->progress_func = ctx->progress_func;
cbtable->progress_baton = ctx->progress_baton;
cbtable->cancel_func = ctx->cancel_func ? cancel_callback : NULL;
cbtable->get_client_string = get_client_string;
+ if (base_dir_abspath)
+ cbtable->get_wc_contents = get_wc_contents;
- cb->base_dir_abspath = base_dir_abspath;
- cb->read_only_wc = read_only_wc;
- cb->pool = pool;
cb->commit_items = commit_items;
cb->ctx = ctx;
- if (base_dir_abspath)
+ if (base_dir_abspath && (read_dav_props || write_dav_props))
{
- svn_error_t *err = svn_wc__node_get_repos_info(NULL, &uuid, ctx->wc_ctx,
+ svn_error_t *err = svn_wc__node_get_repos_info(NULL, NULL, NULL, &uuid,
+ ctx->wc_ctx,
base_dir_abspath,
- pool, pool);
+ result_pool,
+ scratch_pool);
if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY
|| err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
@@ -311,7 +346,29 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
uuid = NULL;
}
else
- SVN_ERR(err);
+ {
+ SVN_ERR(err);
+ cb->base_dir_isversioned = TRUE;
+ }
+ cb->base_dir_abspath = apr_pstrdup(result_pool, base_dir_abspath);
+ }
+
+ if (base_dir_abspath)
+ {
+ svn_error_t *err = svn_wc__get_wcroot(&cb->wcroot_abspath,
+ ctx->wc_ctx, base_dir_abspath,
+ result_pool, scratch_pool);
+
+ if (err)
+ {
+ if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
+ && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
+ && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ cb->wcroot_abspath = NULL;
+ }
}
/* If the caller allows for auto-following redirections, and the
@@ -320,7 +377,7 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
attempts. */
if (corrected_url)
{
- apr_hash_t *attempted = apr_hash_make(pool);
+ apr_hash_t *attempted = apr_hash_make(scratch_pool);
int attempts_left = SVN_CLIENT__MAX_REDIRECT_ATTEMPTS;
*corrected_url = NULL;
@@ -332,7 +389,8 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
don't accept corrected URLs from the RA provider. */
SVN_ERR(svn_ra_open4(ra_session,
attempts_left == 0 ? NULL : &corrected,
- base_url, uuid, cbtable, cb, ctx->config, pool));
+ base_url, uuid, cbtable, cb, ctx->config,
+ result_pool));
/* No error and no corrected URL? We're done here. */
if (! corrected)
@@ -343,117 +401,98 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
{
svn_wc_notify_t *notify =
svn_wc_create_notify_url(corrected,
- svn_wc_notify_url_redirect, pool);
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ svn_wc_notify_url_redirect,
+ scratch_pool);
+ (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
}
/* Our caller will want to know what our final corrected URL was. */
*corrected_url = corrected;
/* Make sure we've not attempted this URL before. */
- if (apr_hash_get(attempted, corrected, APR_HASH_KEY_STRING))
+ if (svn_hash_gets(attempted, corrected))
return svn_error_createf(SVN_ERR_CLIENT_CYCLE_DETECTED, NULL,
_("Redirect cycle detected for URL '%s'"),
corrected);
/* Remember this CORRECTED_URL so we don't wind up in a loop. */
- apr_hash_set(attempted, corrected, APR_HASH_KEY_STRING, (void *)1);
+ svn_hash_sets(attempted, corrected, (void *)1);
base_url = corrected;
}
}
else
{
SVN_ERR(svn_ra_open4(ra_session, NULL, base_url,
- uuid, cbtable, cb, ctx->config, pool));
+ uuid, cbtable, cb, ctx->config, result_pool));
}
return SVN_NO_ERROR;
- }
+}
#undef SVN_CLIENT__MAX_REDIRECT_ATTEMPTS
svn_error_t *
-svn_client_open_ra_session(svn_ra_session_t **session,
- const char *url,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client_open_ra_session2(svn_ra_session_t **session,
+ const char *url,
+ const char *wri_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
return svn_error_trace(
svn_client__open_ra_session_internal(session, NULL, url,
- NULL, NULL, FALSE, TRUE,
- ctx, pool));
+ wri_abspath, NULL,
+ FALSE, FALSE,
+ ctx, result_pool,
+ scratch_pool));
}
-
svn_error_t *
-svn_client_uuid_from_url(const char **uuid,
- const char *url,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client__resolve_rev_and_url(svn_client__pathrev_t **resolved_loc_p,
+ svn_ra_session_t *ra_session,
+ const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
{
- svn_ra_session_t *ra_session;
- apr_pool_t *subpool = svn_pool_create(pool);
-
- /* use subpool to create a temporary RA session */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url,
- NULL, /* no base dir */
- NULL, FALSE, TRUE,
- ctx, subpool));
+ svn_opt_revision_t peg_rev = *peg_revision;
+ svn_opt_revision_t start_rev = *revision;
+ const char *url;
+ svn_revnum_t rev;
- SVN_ERR(svn_ra_get_uuid2(ra_session, uuid, pool));
+ /* Default revisions: peg -> working or head; operative -> peg. */
+ SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev,
+ svn_path_is_url(path_or_url),
+ TRUE /* notice_local_mods */,
+ pool));
- /* destroy the RA session */
- svn_pool_destroy(subpool);
+ /* Run the history function to get the object's (possibly
+ different) url in REVISION. */
+ SVN_ERR(svn_client__repos_locations(&url, &rev, NULL, NULL,
+ ra_session, path_or_url, &peg_rev,
+ &start_rev, NULL, ctx, pool));
+ SVN_ERR(svn_client__pathrev_create_with_session(resolved_loc_p,
+ ra_session, rev, url, pool));
return SVN_NO_ERROR;
}
-
-svn_error_t *
-svn_client_uuid_from_path2(const char **uuid,
- const char *local_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- return svn_error_trace(
- svn_wc__node_get_repos_info(NULL, uuid, ctx->wc_ctx, local_abspath,
- result_pool, scratch_pool));
-}
-
-
-
-
-/* Convert a path or URL for display: if it is a local path, convert it to
- * the local path style; if it is a URL, return it unchanged. */
-static const char *
-path_or_url_local_style(const char *path_or_url,
- apr_pool_t *pool)
-{
- if (svn_path_is_url(path_or_url))
- return path_or_url;
- return svn_dirent_local_style(path_or_url, pool);
-}
-
svn_error_t *
-svn_client__ra_session_from_path(svn_ra_session_t **ra_session_p,
- svn_revnum_t *rev_p,
- const char **url_p,
- const char *path_or_url,
- const char *base_dir_abspath,
- const svn_opt_revision_t *peg_revision_p,
- const svn_opt_revision_t *revision,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client__ra_session_from_path2(svn_ra_session_t **ra_session_p,
+ svn_client__pathrev_t **resolved_loc_p,
+ const char *path_or_url,
+ const char *base_dir_abspath,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
{
svn_ra_session_t *ra_session;
- const char *initial_url, *url;
- svn_opt_revision_t *good_rev;
- svn_opt_revision_t peg_revision, start_rev;
- svn_opt_revision_t dead_end_rev;
- svn_opt_revision_t *ignored_rev;
- svn_revnum_t rev;
- const char *ignored_url, *corrected_url;
+ const char *initial_url;
+ const char *corrected_url;
+ svn_client__pathrev_t *resolved_loc;
+ const char *wri_abspath;
SVN_ERR(svn_client_url_from_path2(&initial_url, path_or_url, ctx, pool,
pool));
@@ -461,48 +500,36 @@ svn_client__ra_session_from_path(svn_ra_session_t **ra_session_p,
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no URL"), path_or_url);
- start_rev = *revision;
- peg_revision = *peg_revision_p;
- SVN_ERR(svn_opt_resolve_revisions(&peg_revision, &start_rev,
- svn_path_is_url(path_or_url),
- TRUE,
- pool));
+ if (base_dir_abspath)
+ wri_abspath = base_dir_abspath;
+ else if (!svn_path_is_url(path_or_url))
+ SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url, pool));
+ else
+ wri_abspath = NULL;
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
initial_url,
- base_dir_abspath, NULL,
+ wri_abspath,
+ NULL /* commit_items */,
base_dir_abspath != NULL,
- FALSE, ctx, pool));
+ base_dir_abspath != NULL,
+ ctx, pool, pool));
/* If we got a CORRECTED_URL, we'll want to refer to that as the
URL-ized form of PATH_OR_URL from now on. */
if (corrected_url && svn_path_is_url(path_or_url))
path_or_url = corrected_url;
- dead_end_rev.kind = svn_opt_revision_unspecified;
-
- /* Run the history function to get the object's (possibly
- different) url in REVISION. */
- SVN_ERR(svn_client__repos_locations(&url, &good_rev,
- &ignored_url, &ignored_rev,
- ra_session,
- path_or_url, &peg_revision,
- /* search range: */
- &start_rev, &dead_end_rev,
- ctx, pool));
+ SVN_ERR(svn_client__resolve_rev_and_url(&resolved_loc, ra_session,
+ path_or_url, peg_revision, revision,
+ ctx, pool));
/* Make the session point to the real URL. */
- SVN_ERR(svn_ra_reparent(ra_session, url, pool));
-
- /* Resolve good_rev into a real revnum. */
- if (good_rev->kind == svn_opt_revision_unspecified)
- good_rev->kind = svn_opt_revision_head;
- SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx, url,
- ra_session, good_rev, pool));
+ SVN_ERR(svn_ra_reparent(ra_session, resolved_loc->url, pool));
*ra_session_p = ra_session;
- *rev_p = rev;
- *url_p = url;
+ if (resolved_loc_p)
+ *resolved_loc_p = resolved_loc;
return SVN_NO_ERROR;
}
@@ -514,7 +541,6 @@ svn_client__ensure_ra_session_url(const char **old_session_url,
const char *session_url,
apr_pool_t *pool)
{
- *old_session_url = NULL;
SVN_ERR(svn_ra_get_session_url(ra_session, old_session_url, pool));
if (! session_url)
SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_url, pool));
@@ -565,7 +591,7 @@ compare_segments(const void *a, const void *b)
svn_error_t *
svn_client__repos_location_segments(apr_array_header_t **segments,
svn_ra_session_t *ra_session,
- const char *path,
+ const char *url,
svn_revnum_t peg_revision,
svn_revnum_t start_revision,
svn_revnum_t end_revision,
@@ -573,25 +599,139 @@ svn_client__repos_location_segments(apr_array_header_t **segments,
apr_pool_t *pool)
{
struct gls_receiver_baton_t gls_receiver_baton;
+ const char *old_session_url;
+ svn_error_t *err;
+
*segments = apr_array_make(pool, 8, sizeof(svn_location_segment_t *));
gls_receiver_baton.segments = *segments;
gls_receiver_baton.ctx = ctx;
gls_receiver_baton.pool = pool;
- SVN_ERR(svn_ra_get_location_segments(ra_session, path, peg_revision,
- start_revision, end_revision,
- gls_receiver, &gls_receiver_baton,
- pool));
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+ url, pool));
+ err = svn_ra_get_location_segments(ra_session, "", peg_revision,
+ start_revision, end_revision,
+ gls_receiver, &gls_receiver_baton,
+ pool);
+ SVN_ERR(svn_error_compose_create(
+ err, svn_ra_reparent(ra_session, old_session_url, pool)));
qsort((*segments)->elts, (*segments)->nelts,
(*segments)->elt_size, compare_segments);
return SVN_NO_ERROR;
}
+/* Set *START_URL and *END_URL to the URLs that the object URL@PEG_REVNUM
+ * had in revisions START_REVNUM and END_REVNUM. Return an error if the
+ * node cannot be traced back to one of the requested revisions.
+ *
+ * START_URL and/or END_URL may be NULL if not wanted. START_REVNUM and
+ * END_REVNUM must be valid revision numbers except that END_REVNUM may
+ * be SVN_INVALID_REVNUM if END_URL is NULL.
+ *
+ * RA_SESSION is an open RA session parented at URL.
+ */
+static svn_error_t *
+repos_locations(const char **start_url,
+ const char **end_url,
+ svn_ra_session_t *ra_session,
+ const char *url,
+ svn_revnum_t peg_revnum,
+ svn_revnum_t start_revnum,
+ svn_revnum_t end_revnum,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *repos_url, *start_path, *end_path;
+ apr_array_header_t *revs;
+ apr_hash_t *rev_locs;
+
+ SVN_ERR_ASSERT(peg_revnum != SVN_INVALID_REVNUM);
+ SVN_ERR_ASSERT(start_revnum != SVN_INVALID_REVNUM);
+ SVN_ERR_ASSERT(end_revnum != SVN_INVALID_REVNUM || end_url == NULL);
+
+ /* Avoid a network request in the common easy case. */
+ if (start_revnum == peg_revnum
+ && (end_revnum == peg_revnum || end_revnum == SVN_INVALID_REVNUM))
+ {
+ if (start_url)
+ *start_url = apr_pstrdup(result_pool, url);
+ if (end_url)
+ *end_url = apr_pstrdup(result_pool, url);
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, scratch_pool));
+
+ revs = apr_array_make(scratch_pool, 2, sizeof(svn_revnum_t));
+ APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum;
+ if (end_revnum != start_revnum && end_revnum != SVN_INVALID_REVNUM)
+ APR_ARRAY_PUSH(revs, svn_revnum_t) = end_revnum;
+
+ SVN_ERR(svn_ra_get_locations(ra_session, &rev_locs, "", peg_revnum,
+ revs, scratch_pool));
+
+ /* We'd better have all the paths we were looking for! */
+ if (start_url)
+ {
+ start_path = apr_hash_get(rev_locs, &start_revnum, sizeof(svn_revnum_t));
+ if (! start_path)
+ return svn_error_createf
+ (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("Unable to find repository location for '%s' in revision %ld"),
+ url, start_revnum);
+ *start_url = svn_path_url_add_component2(repos_url, start_path + 1,
+ result_pool);
+ }
+
+ if (end_url)
+ {
+ end_path = apr_hash_get(rev_locs, &end_revnum, sizeof(svn_revnum_t));
+ if (! end_path)
+ return svn_error_createf
+ (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("The location for '%s' for revision %ld does not exist in the "
+ "repository or refers to an unrelated object"),
+ url, end_revnum);
+
+ *end_url = svn_path_url_add_component2(repos_url, end_path + 1,
+ result_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__repos_location(svn_client__pathrev_t **op_loc_p,
+ svn_ra_session_t *ra_session,
+ const svn_client__pathrev_t *peg_loc,
+ svn_revnum_t op_revnum,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *old_session_url;
+ const char *op_url;
+ svn_error_t *err;
+
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+ peg_loc->url, scratch_pool));
+ err = repos_locations(&op_url, NULL, ra_session,
+ peg_loc->url, peg_loc->rev,
+ op_revnum, SVN_INVALID_REVNUM,
+ result_pool, scratch_pool);
+ SVN_ERR(svn_error_compose_create(
+ err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)));
+
+ *op_loc_p = svn_client__pathrev_create(peg_loc->repos_root_url,
+ peg_loc->repos_uuid,
+ op_revnum, op_url, result_pool);
+ return SVN_NO_ERROR;
+}
svn_error_t *
svn_client__repos_locations(const char **start_url,
- svn_opt_revision_t **start_revision,
+ svn_revnum_t *start_revision,
const char **end_url,
- svn_opt_revision_t **end_revision,
+ svn_revnum_t *end_revision,
svn_ra_session_t *ra_session,
const char *path,
const svn_opt_revision_t *revision,
@@ -600,16 +740,11 @@ svn_client__repos_locations(const char **start_url,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- const char *repos_url;
const char *url;
- const char *start_path = NULL;
- const char *end_path = NULL;
const char *local_abspath_or_url;
svn_revnum_t peg_revnum = SVN_INVALID_REVNUM;
svn_revnum_t start_revnum, end_revnum;
svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
- apr_array_header_t *revs;
- apr_hash_t *rev_locs;
apr_pool_t *subpool = svn_pool_create(pool);
/* Ensure that we are given some real revision data to work with.
@@ -619,9 +754,17 @@ svn_client__repos_locations(const char **start_url,
|| start->kind == svn_opt_revision_unspecified)
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
- /* Check to see if this is schedule add with history working copy
- path. If it is, then we need to use the URL and peg revision of
- the copyfrom information. */
+ if (end == NULL)
+ {
+ static const svn_opt_revision_t unspecified_rev
+ = { svn_opt_revision_unspecified, { 0 } };
+
+ end = &unspecified_rev;
+ }
+
+ /* Determine LOCAL_ABSPATH_OR_URL, URL, and possibly PEG_REVNUM.
+ If we are looking at the working version of a WC path that is scheduled
+ as a copy, then we need to use the copy-from URL and peg revision. */
if (! svn_path_is_url(path))
{
SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, subpool));
@@ -682,9 +825,8 @@ svn_client__repos_locations(const char **start_url,
/* Open a RA session to this URL if we don't have one already. */
if (! ra_session)
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url, NULL,
- NULL, FALSE, TRUE,
- ctx, subpool));
+ SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
+ ctx, subpool, subpool));
/* Resolve the opt_revision_ts. */
if (peg_revnum == SVN_INVALID_REVNUM)
@@ -703,110 +845,53 @@ svn_client__repos_locations(const char **start_url,
ra_session, end, pool));
/* Set the output revision variables. */
- *start_revision = apr_pcalloc(pool, sizeof(**start_revision));
- (*start_revision)->kind = svn_opt_revision_number;
- (*start_revision)->value.number = start_revnum;
- if (end->kind != svn_opt_revision_unspecified)
+ if (start_revision)
{
- *end_revision = apr_pcalloc(pool, sizeof(**end_revision));
- (*end_revision)->kind = svn_opt_revision_number;
- (*end_revision)->value.number = end_revnum;
+ *start_revision = start_revnum;
}
-
- if (start_revnum == peg_revnum && end_revnum == peg_revnum)
+ if (end_revision && end->kind != svn_opt_revision_unspecified)
{
- /* Avoid a network request in the common easy case. */
- *start_url = url;
- if (end->kind != svn_opt_revision_unspecified)
- *end_url = url;
- svn_pool_destroy(subpool);
- return SVN_NO_ERROR;
+ *end_revision = end_revnum;
}
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, subpool));
-
- revs = apr_array_make(subpool, 2, sizeof(svn_revnum_t));
- APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum;
- if (end_revnum != start_revnum)
- APR_ARRAY_PUSH(revs, svn_revnum_t) = end_revnum;
-
- SVN_ERR(svn_ra_get_locations(ra_session, &rev_locs, "", peg_revnum,
- revs, subpool));
-
- /* We'd better have all the paths we were looking for! */
- start_path = apr_hash_get(rev_locs, &start_revnum, sizeof(svn_revnum_t));
- if (! start_path)
- return svn_error_createf
- (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
- _("Unable to find repository location for '%s' in revision %ld"),
- path_or_url_local_style(path, pool), start_revnum);
-
- end_path = apr_hash_get(rev_locs, &end_revnum, sizeof(svn_revnum_t));
- if (! end_path)
- return svn_error_createf
- (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
- _("The location for '%s' for revision %ld does not exist in the "
- "repository or refers to an unrelated object"),
- path_or_url_local_style(path, pool), end_revnum);
-
- /* Set our return variables */
- *start_url = svn_path_url_add_component2(repos_url, start_path + 1, pool);
- if (end->kind != svn_opt_revision_unspecified)
- *end_url = svn_path_url_add_component2(repos_url, end_path + 1, pool);
-
+ SVN_ERR(repos_locations(start_url, end_url,
+ ra_session, url, peg_revnum,
+ start_revnum, end_revnum,
+ pool, subpool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
-
svn_error_t *
-svn_client__get_youngest_common_ancestor(const char **ancestor_path,
- svn_revnum_t *ancestor_revision,
- const char *path_or_url1,
- svn_revnum_t rev1,
- const char *path_or_url2,
- svn_revnum_t rev2,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+svn_client__calc_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
+ const svn_client__pathrev_t *loc1,
+ apr_hash_t *history1,
+ svn_boolean_t has_rev_zero_history1,
+ const svn_client__pathrev_t *loc2,
+ apr_hash_t *history2,
+ svn_boolean_t has_rev_zero_history2,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- apr_hash_t *history1, *history2;
apr_hash_index_t *hi;
svn_revnum_t yc_revision = SVN_INVALID_REVNUM;
- const char *yc_path = NULL;
- svn_opt_revision_t revision1, revision2;
- svn_boolean_t has_rev_zero_history1;
- svn_boolean_t has_rev_zero_history2;
+ const char *yc_relpath = NULL;
- revision1.kind = revision2.kind = svn_opt_revision_number;
- revision1.value.number = rev1;
- revision2.value.number = rev2;
-
- /* We're going to cheat and use history-as-mergeinfo because it
- saves us a bunch of annoying custom data comparisons and such. */
- SVN_ERR(svn_client__get_history_as_mergeinfo(&history1,
- &has_rev_zero_history1,
- path_or_url1,
- &revision1,
- SVN_INVALID_REVNUM,
- SVN_INVALID_REVNUM,
- NULL, ctx, pool));
- SVN_ERR(svn_client__get_history_as_mergeinfo(&history2,
- &has_rev_zero_history2,
- path_or_url2,
- &revision2,
- SVN_INVALID_REVNUM,
- SVN_INVALID_REVNUM,
- NULL, ctx, pool));
+ if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0)
+ {
+ *ancestor_p = NULL;
+ return SVN_NO_ERROR;
+ }
/* Loop through the first location's history, check for overlapping
paths and ranges in the second location's history, and
remembering the youngest matching location. */
- for (hi = apr_hash_first(pool, history1); hi; hi = apr_hash_next(hi))
+ for (hi = apr_hash_first(scratch_pool, history1); hi; hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
apr_ssize_t path_len = svn__apr_hash_index_klen(hi);
- apr_array_header_t *ranges1 = svn__apr_hash_index_val(hi);
- apr_array_header_t *ranges2, *common;
+ svn_rangelist_t *ranges1 = svn__apr_hash_index_val(hi);
+ svn_rangelist_t *ranges2, *common;
ranges2 = apr_hash_get(history2, path, path_len);
if (ranges2)
@@ -814,7 +899,7 @@ svn_client__get_youngest_common_ancestor(const char **ancestor_path,
/* We have a path match. Now, did our two histories share
any revisions at that path? */
SVN_ERR(svn_rangelist_intersect(&common, ranges1, ranges2,
- TRUE, pool));
+ TRUE, scratch_pool));
if (common->nelts)
{
svn_merge_range_t *yc_range =
@@ -823,7 +908,7 @@ svn_client__get_youngest_common_ancestor(const char **ancestor_path,
|| (yc_range->end > yc_revision))
{
yc_revision = yc_range->end;
- yc_path = path + 1;
+ yc_relpath = path + 1;
}
}
}
@@ -831,13 +916,260 @@ svn_client__get_youngest_common_ancestor(const char **ancestor_path,
/* It's possible that PATH_OR_URL1 and PATH_OR_URL2's only common
history is revision 0. */
- if (!yc_path && has_rev_zero_history1 && has_rev_zero_history2)
+ if (!yc_relpath && has_rev_zero_history1 && has_rev_zero_history2)
{
- yc_path = "/";
+ yc_relpath = "";
yc_revision = 0;
}
- *ancestor_path = yc_path;
- *ancestor_revision = yc_revision;
+ if (yc_relpath)
+ {
+ *ancestor_p = svn_client__pathrev_create_with_relpath(
+ loc1->repos_root_url, loc1->repos_uuid,
+ yc_revision, yc_relpath, result_pool);
+ }
+ else
+ {
+ *ancestor_p = NULL;
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
+ const svn_client__pathrev_t *loc1,
+ const svn_client__pathrev_t *loc2,
+ svn_ra_session_t *session,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *sesspool = NULL;
+ apr_hash_t *history1, *history2;
+ svn_boolean_t has_rev_zero_history1;
+ svn_boolean_t has_rev_zero_history2;
+
+ if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0)
+ {
+ *ancestor_p = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ /* Open an RA session for the two locations. */
+ if (session == NULL)
+ {
+ sesspool = svn_pool_create(scratch_pool);
+ SVN_ERR(svn_client_open_ra_session2(&session, loc1->url, NULL, ctx,
+ sesspool, sesspool));
+ }
+
+ /* We're going to cheat and use history-as-mergeinfo because it
+ saves us a bunch of annoying custom data comparisons and such. */
+ SVN_ERR(svn_client__get_history_as_mergeinfo(&history1,
+ &has_rev_zero_history1,
+ loc1,
+ SVN_INVALID_REVNUM,
+ SVN_INVALID_REVNUM,
+ session, ctx, scratch_pool));
+ SVN_ERR(svn_client__get_history_as_mergeinfo(&history2,
+ &has_rev_zero_history2,
+ loc2,
+ SVN_INVALID_REVNUM,
+ SVN_INVALID_REVNUM,
+ session, ctx, scratch_pool));
+ /* Close the ra session if we opened one. */
+ if (sesspool)
+ svn_pool_destroy(sesspool);
+
+ SVN_ERR(svn_client__calc_youngest_common_ancestor(ancestor_p,
+ loc1, history1,
+ has_rev_zero_history1,
+ loc2, history2,
+ has_rev_zero_history2,
+ result_pool,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__youngest_common_ancestor(const char **ancestor_url,
+ svn_revnum_t *ancestor_rev,
+ const char *path_or_url1,
+ const svn_opt_revision_t *revision1,
+ const char *path_or_url2,
+ const svn_opt_revision_t *revision2,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *sesspool = svn_pool_create(scratch_pool);
+ svn_ra_session_t *session;
+ svn_client__pathrev_t *loc1, *loc2, *ancestor;
+
+ /* Resolve the two locations */
+ SVN_ERR(svn_client__ra_session_from_path2(&session, &loc1,
+ path_or_url1, NULL,
+ revision1, revision1,
+ ctx, sesspool));
+ SVN_ERR(svn_client__resolve_rev_and_url(&loc2, session,
+ path_or_url2, revision2, revision2,
+ ctx, scratch_pool));
+
+ SVN_ERR(svn_client__get_youngest_common_ancestor(
+ &ancestor, loc1, loc2, session, ctx, result_pool, scratch_pool));
+
+ if (ancestor)
+ {
+ *ancestor_url = ancestor->url;
+ *ancestor_rev = ancestor->rev;
+ }
+ else
+ {
+ *ancestor_url = NULL;
+ *ancestor_rev = SVN_INVALID_REVNUM;
+ }
+ svn_pool_destroy(sesspool);
+ return SVN_NO_ERROR;
+}
+
+
+struct ra_ev2_baton {
+ /* The working copy context, from the client context. */
+ svn_wc_context_t *wc_ctx;
+
+ /* For a given REPOS_RELPATH, provide a LOCAL_ABSPATH that represents
+ that repository node. */
+ apr_hash_t *relpath_map;
+};
+
+
+svn_error_t *
+svn_client__ra_provide_base(svn_stream_t **contents,
+ svn_revnum_t *revision,
+ void *baton,
+ const char *repos_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct ra_ev2_baton *reb = baton;
+ const char *local_abspath;
+ svn_error_t *err;
+
+ local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
+ if (!local_abspath)
+ {
+ *contents = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ err = svn_wc_get_pristine_contents2(contents, reb->wc_ctx, local_abspath,
+ result_pool, scratch_pool);
+ if (err)
+ {
+ if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ *contents = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ if (*contents != NULL)
+ {
+ /* The pristine contents refer to the BASE, or to the pristine of
+ a copy/move to this location. Fetch the correct revision. */
+ SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL,
+ reb->wc_ctx, local_abspath, FALSE,
+ scratch_pool, scratch_pool));
+ }
+
return SVN_NO_ERROR;
}
+
+
+svn_error_t *
+svn_client__ra_provide_props(apr_hash_t **props,
+ svn_revnum_t *revision,
+ void *baton,
+ const char *repos_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct ra_ev2_baton *reb = baton;
+ const char *local_abspath;
+ svn_error_t *err;
+
+ local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
+ if (!local_abspath)
+ {
+ *props = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ err = svn_wc_get_pristine_props(props, reb->wc_ctx, local_abspath,
+ result_pool, scratch_pool);
+ if (err)
+ {
+ if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ *props = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ if (*props != NULL)
+ {
+ /* The pristine props refer to the BASE, or to the pristine props of
+ a copy/move to this location. Fetch the correct revision. */
+ SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL,
+ reb->wc_ctx, local_abspath, FALSE,
+ scratch_pool, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_client__ra_get_copysrc_kind(svn_node_kind_t *kind,
+ void *baton,
+ const char *repos_relpath,
+ svn_revnum_t src_revision,
+ apr_pool_t *scratch_pool)
+{
+ struct ra_ev2_baton *reb = baton;
+ const char *local_abspath;
+
+ local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
+ if (!local_abspath)
+ {
+ *kind = svn_node_unknown;
+ return SVN_NO_ERROR;
+ }
+
+ /* ### what to do with SRC_REVISION? */
+
+ SVN_ERR(svn_wc_read_kind2(kind, reb->wc_ctx, local_abspath,
+ FALSE, FALSE, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+void *
+svn_client__ra_make_cb_baton(svn_wc_context_t *wc_ctx,
+ apr_hash_t *relpath_map,
+ apr_pool_t *result_pool)
+{
+ struct ra_ev2_baton *reb = apr_palloc(result_pool, sizeof(*reb));
+
+ SVN_ERR_ASSERT_NO_RETURN(wc_ctx != NULL);
+ SVN_ERR_ASSERT_NO_RETURN(relpath_map != NULL);
+
+ reb->wc_ctx = wc_ctx;
+ reb->relpath_map = relpath_map;
+
+ return reb;
+}