You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by da...@apache.org on 2012/03/30 15:55:31 UTC
svn commit: r1307424 [3/11] - in /subversion/branches/revprop-packing: ./
build/ac-macros/ notes/ notes/directory-index/ notes/wc-ng/
subversion/bindings/javahl/ subversion/bindings/swig/python/svn/
subversion/bindings/swig/python/tests/ subversion/bin...
Modified: subversion/branches/revprop-packing/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/libsvn_client/merge.c?rev=1307424&r1=1307423&r2=1307424&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/revprop-packing/subversion/libsvn_client/merge.c Fri Mar 30 13:55:26 2012
@@ -58,6 +58,7 @@
#include "private/svn_mergeinfo_private.h"
#include "private/svn_fspath.h"
#include "private/svn_ra_private.h"
+#include "private/svn_client_private.h"
#include "svn_private_config.h"
@@ -164,16 +165,24 @@ typedef struct url_uuid_t
const char *uuid;
} url_uuid_t;
+/* A location in a repository. */
+typedef struct repo_location_t
+{
+ url_uuid_t *repo;
+ svn_revnum_t rev;
+ const char *url;
+} repo_location_t;
+
/* */
typedef struct merge_source_t
{
/* "left" side URL and revision (inclusive iff youngest) */
- const char *url1;
- svn_revnum_t rev1;
+ /* The "repo" field is not currently initialized or used. */
+ repo_location_t loc1;
/* "right" side URL and revision (inclusive iff youngest) */
- const char *url2;
- svn_revnum_t rev2;
+ /* The "repo" field is not currently initialized or used. */
+ repo_location_t loc2;
} merge_source_t;
@@ -186,14 +195,10 @@ typedef struct merge_target_t
/* Node kind of the WC node (at the start of the merge) */
svn_node_kind_t kind;
- /* URL of the node, or NULL if node is locally added */
- const char *url;
-
- /* Revision of the node, or NULL if node is locally added */
- svn_revnum_t rev;
-
- /* Repository root URL and UUID, even if node is locally added */
- url_uuid_t repos_root;
+ /* The repository location of the base node of the target WC. If the node
+ * is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM. REPO
+ * is always valid. */
+ repo_location_t loc;
} merge_target_t;
@@ -331,12 +336,12 @@ check_repos_match(merge_cmd_baton_t *mer
const char *url,
apr_pool_t *scratch_pool)
{
- if (!svn_uri__is_ancestor(merge_b->target->repos_root.url, url))
+ if (!svn_uri__is_ancestor(merge_b->target->loc.repo->url, url))
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Url '%s' of '%s' is not in repository '%s'"),
url, svn_dirent_local_style(local_abspath, scratch_pool),
- merge_b->target->repos_root.url);
+ merge_b->target->loc.repo->url);
return SVN_NO_ERROR;
}
@@ -527,21 +532,21 @@ make_conflict_versions(const svn_wc_conf
const char *child = svn_dirent_skip_ancestor(merge_b->target->abspath,
victim_abspath);
SVN_ERR_ASSERT(child != NULL);
- left_url = svn_path_url_add_component2(merge_b->merge_source.url1,
+ left_url = svn_path_url_add_component2(merge_b->merge_source.loc1.url,
child, merge_b->pool);
- right_url = svn_path_url_add_component2(merge_b->merge_source.url2,
+ right_url = svn_path_url_add_component2(merge_b->merge_source.loc2.url,
child, merge_b->pool);
}
*left = svn_wc_conflict_version_create(
src_repos_url,
svn_uri_skip_ancestor(src_repos_url, left_url, merge_b->pool),
- merge_b->merge_source.rev1, node_kind, merge_b->pool);
+ merge_b->merge_source.loc1.rev, node_kind, merge_b->pool);
*right = svn_wc_conflict_version_create(
src_repos_url,
svn_uri_skip_ancestor(src_repos_url, right_url, merge_b->pool),
- merge_b->merge_source.rev2, node_kind, merge_b->pool);
+ merge_b->merge_source.loc2.rev, node_kind, merge_b->pool);
return SVN_NO_ERROR;
}
@@ -1171,7 +1176,7 @@ merge_props_changed(svn_wc_notify_state_
http://svn.haxx.se/dev/archive-2008-09/0006.shtml. If the
merge sources are not ancestral then there is no concept of a
'forward' or 'reverse' merge and we filter unconditionally. */
- if (merge_b->merge_source.rev1 < merge_b->merge_source.rev2
+ if (merge_b->merge_source.loc1.rev < merge_b->merge_source.loc2.rev
|| !merge_b->sources_ancestral)
SVN_ERR(filter_self_referential_mergeinfo(&props,
local_abspath,
@@ -1733,13 +1738,13 @@ merge_file_added(svn_wc_notify_state_t *
merge-from-foreign-repository scenario) or wrong place in the
right repository (in the same-repos scenario). So we'll
strip them. (Is this a layering violation?) */
- if (svn_property_kind(NULL, prop->name) == svn_prop_wc_kind)
+ if (svn_property_kind2(prop->name) == svn_prop_wc_kind)
continue;
/* And in the foreign repository merge case, we only want
regular properties. */
if ((! merge_b->same_repos)
- && (svn_property_kind(NULL, prop->name) != svn_prop_regular_kind))
+ && (svn_property_kind2(prop->name) != svn_prop_regular_kind))
continue;
/* Issue #3383: We don't want mergeinfo from a foreign repository. */
@@ -1799,7 +1804,7 @@ merge_file_added(svn_wc_notify_state_t *
mine_abspath);
SVN_ERR_ASSERT(child != NULL);
copyfrom_url = svn_path_url_add_component2(
- merge_b->merge_source.url2,
+ merge_b->merge_source.loc2.url,
child, scratch_pool);
copyfrom_rev = rev2;
SVN_ERR(check_repos_match(merge_b, mine_abspath, copyfrom_url,
@@ -2194,7 +2199,7 @@ merge_dir_added(svn_wc_notify_state_t *s
add. */
if (merge_b->same_repos)
{
- copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.url2,
+ copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2.url,
child, scratch_pool);
copyfrom_rev = rev;
@@ -3356,7 +3361,7 @@ fix_deleted_subtree_ranges(const merge_s
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- svn_boolean_t is_rollback = source->rev2 < source->rev1;
+ svn_boolean_t is_rollback = source->loc2.rev < source->loc1.rev;
/* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
start at index 1 to examine only subtrees. */
@@ -3430,12 +3435,13 @@ fix_deleted_subtree_ranges(const merge_s
SVN_ERR_ASSERT(child_repos_src_path);
child_primary_source_url =
- svn_path_url_add_component2((source->rev1 < source->rev2)
- ? source->url2 : source->url1,
+ svn_path_url_add_component2((source->loc1.rev < source->loc2.rev)
+ ? source->loc2.url : source->loc1.url,
child_repos_src_path, iterpool);
SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
- source->rev1, source->rev2,
+ source->loc1.rev,
+ source->loc2.rev,
child_primary_source_url,
ra_session,
merge_b->ctx, result_pool,
@@ -3476,8 +3482,8 @@ fix_deleted_subtree_ranges(const merge_s
is older than START, then the base revision is used as the younger
bound in place of START.
- RA_SESSION is an open RA session to the repository in which SOURCE lives.
- It may be temporarily reparented as needed by this function.
+ RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH
+ lives. It may be temporarily reparented as needed by this function.
Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
Use SCRATCH_POOL for any temporary allocations. */
@@ -3508,9 +3514,8 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
if (implicit_mergeinfo)
{
- const char *repos_root;
- const char *repos_relpath;
svn_revnum_t target_rev;
+ const char *target_url;
/* Assert that we have sane input. */
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
@@ -3518,18 +3523,15 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
/* Retrieve the origin (original_*) of the node, or just the
url if the node was not copied. */
- SVN_ERR(svn_wc__node_get_origin(FALSE, &target_rev, &repos_relpath,
- &repos_root, NULL, NULL,
- ctx->wc_ctx, target_abspath, FALSE,
- scratch_pool, scratch_pool));
-
- if (! repos_relpath)
- {
- /* We've been asked to operate on a target which has no location
- * in the repository. Either it's unversioned (but attempts to
- * merge into unversioned targets should not get as far as here),
- * or it is locally added, in which case the target's implicit
- * mergeinfo is empty. */
+ SVN_ERR(svn_client__wc_node_get_origin(NULL, NULL,
+ &target_rev, &target_url,
+ target_abspath, ctx,
+ scratch_pool, scratch_pool));
+
+ if (! target_url)
+ {
+ /* We've been asked to operate on a locally added target, so its
+ * implicit mergeinfo is empty. */
*implicit_mergeinfo = apr_hash_make(result_pool);
}
else if (target_rev <= end)
@@ -3540,11 +3542,6 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
}
else
{
- const char *url;
-
- url = svn_path_url_add_component2(repos_root, repos_relpath,
- scratch_pool);
-
/* Fetch so-called "implicit mergeinfo" (that is, natural
history). */
@@ -3558,7 +3555,7 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
/* Fetch the implicit mergeinfo. */
SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
NULL,
- url, target_rev,
+ target_url, target_rev,
start, end,
ra_session, ctx,
result_pool));
@@ -3570,7 +3567,7 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
/* Helper for ensure_implicit_mergeinfo().
- PARENT, CHILD, REVISION1, REVISION2, RA_SESSION, and CTX
+ PARENT, CHILD, REVISION1, REVISION2 and CTX
are all cascaded from the arguments of the same names in
ensure_implicit_mergeinfo(). PARENT and CHILD must both exist, i.e.
this function should never be called where CHILD is the merge target.
@@ -3580,6 +3577,9 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
PARENT->IMPLICIT_MERGEINFO. CHILD->IMPLICIT_MERGEINFO is allocated
in RESULT_POOL.
+
+ RA_SESSION is an RA session open to the repository that contains CHILD.
+ It may be temporarily reparented by this function.
*/
static svn_error_t *
inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
@@ -3630,7 +3630,10 @@ inherit_implicit_mergeinfo_from_parent(s
PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository. Use
SCRATCH_POOL for all temporary allocations.
- PARENT, CHILD, REVISION1, REVISION2, RA_SESSION, and
+ RA_SESSION is an RA session open to the repository that contains CHILD.
+ It may be temporarily reparented by this function.
+
+ PARENT, CHILD, REVISION1, REVISION2 and
CTX are all cascaded from the arguments of the same name in
filter_merged_revisions() and the same conditions for that function
hold here. */
@@ -3986,8 +3989,8 @@ calculate_remaining_ranges(svn_client__m
apr_pool_t *scratch_pool)
{
const char *mergeinfo_path;
- const char *primary_url = (source->rev1 < source->rev2)
- ? source->url2 : source->url1;
+ const char *primary_url = (source->loc1.rev < source->loc2.rev)
+ ? source->loc2.url : source->loc1.url;
svn_mergeinfo_t adjusted_target_mergeinfo = NULL;
svn_revnum_t child_base_revision;
@@ -4027,7 +4030,7 @@ calculate_remaining_ranges(svn_client__m
merged (or, in the case of reverse merges, ranges not yet merged). */
SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
adjusted_target_mergeinfo,
- source->rev1, source->rev2,
+ source->loc1.rev, source->loc2.rev,
child_inherits_implicit,
ra_session, ctx, result_pool,
scratch_pool));
@@ -4064,8 +4067,8 @@ calculate_remaining_ranges(svn_client__m
can't have any "future" history. */
if (SVN_IS_VALID_REVNUM(child_base_revision)
&& ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */
- && (source->rev2 < source->rev1) /* Reverse merge */
- && (child_base_revision <= source->rev2)) /* From CHILD's future */
+ && (source->loc2.rev < source->loc1.rev) /* Reverse merge */
+ && (child_base_revision <= source->loc2.rev)) /* From CHILD's future */
{
/* Hmmm, an inoperative reverse merge from the "future". If it is
from our own future return a helpful error. */
@@ -4073,7 +4076,9 @@ calculate_remaining_ranges(svn_client__m
const char *start_url;
err = svn_client__repos_location(&start_url,
- ra_session, source->url1, source->rev1,
+ ra_session,
+ source->loc1.url,
+ source->loc1.rev,
child_base_revision,
ctx, scratch_pool, scratch_pool);
if (err)
@@ -4136,10 +4141,10 @@ find_gaps_in_merge_source_history(svn_re
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t implicit_src_mergeinfo;
- svn_revnum_t young_rev = MAX(source->rev1, source->rev2);
- svn_revnum_t old_rev = MIN(source->rev1, source->rev2);
- const char *primary_url = (source->rev1 < source->rev2)
- ? source->url2 : source->url1;
+ svn_revnum_t young_rev = MAX(source->loc1.rev, source->loc2.rev);
+ svn_revnum_t old_rev = MIN(source->loc1.rev, source->loc2.rev);
+ const char *primary_url = (source->loc1.rev < source->loc2.rev)
+ ? source->loc2.url : source->loc1.url;
const char *merge_src_fspath;
apr_array_header_t *rangelist;
@@ -4188,7 +4193,7 @@ find_gaps_in_merge_source_history(svn_re
/* As mentioned above, multiple gaps *shouldn't* be possible. */
SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
- *gap_start = MIN(source->rev1, source->rev2);
+ *gap_start = MIN(source->loc1.rev, source->loc2.rev);
*gap_end = (APR_ARRAY_IDX(rangelist,
rangelist->nelts - 1,
svn_merge_range_t *))->start;
@@ -4196,8 +4201,8 @@ find_gaps_in_merge_source_history(svn_re
else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
{
apr_array_header_t *requested_rangelist =
- svn_rangelist__initialize(MIN(source->rev1, source->rev2),
- MAX(source->rev1, source->rev2),
+ svn_rangelist__initialize(MIN(source->loc1.rev, source->loc2.rev),
+ MAX(source->loc1.rev, source->loc2.rev),
TRUE, scratch_pool);
apr_array_header_t *implicit_rangelist =
apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
@@ -4286,8 +4291,10 @@ populate_remaining_ranges(apr_array_head
NULL, /* child->inherited_mergeinfo */
svn_mergeinfo_inherited, ra_session,
child->abspath,
- MAX(source->rev1, source->rev2),
- MIN(source->rev1, source->rev2),
+ MAX(source->loc1.rev,
+ source->loc2.rev),
+ MIN(source->loc1.rev,
+ source->loc2.rev),
merge_b->ctx, result_pool,
iterpool));
}
@@ -4308,13 +4315,14 @@ populate_remaining_ranges(apr_array_head
child_inherits_implicit = (parent && !child->switched);
SVN_ERR(ensure_implicit_mergeinfo(parent, child,
child_inherits_implicit,
- source->rev1, source->rev2,
+ source->loc1.rev,
+ source->loc2.rev,
ra_session, merge_b->ctx,
result_pool, iterpool));
}
- child->remaining_ranges = svn_rangelist__initialize(source->rev1,
- source->rev2,
+ child->remaining_ranges = svn_rangelist__initialize(source->loc1.rev,
+ source->loc2.rev,
TRUE,
result_pool);
}
@@ -4366,10 +4374,10 @@ populate_remaining_ranges(apr_array_head
child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
child->abspath);
SVN_ERR_ASSERT(child_repos_path != NULL);
- child_source.url1 = svn_path_url_add_component2(source->url1, child_repos_path,
- iterpool);
- child_source.url2 = svn_path_url_add_component2(source->url2, child_repos_path,
- iterpool);
+ child_source.loc1.url = svn_path_url_add_component2(
+ source->loc1.url, child_repos_path, iterpool);
+ child_source.loc2.url = svn_path_url_add_component2(
+ source->loc2.url, child_repos_path, iterpool);
/* Get the explicit/inherited mergeinfo for CHILD. If CHILD is the
merge target then also get its implicit mergeinfo. Otherwise defer
@@ -4382,8 +4390,8 @@ populate_remaining_ranges(apr_array_head
&(child->inherited_mergeinfo),
svn_mergeinfo_inherited, ra_session,
child->abspath,
- MAX(source->rev1, source->rev2),
- MIN(source->rev1, source->rev2),
+ MAX(source->loc1.rev, source->loc2.rev),
+ MIN(source->loc1.rev, source->loc2.rev),
merge_b->ctx, result_pool, iterpool));
/* If CHILD isn't the merge target find its parent. */
@@ -4434,7 +4442,7 @@ populate_remaining_ranges(apr_array_head
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES
so it will work with the svn_rangelist_* APIs below. */
- if (source->rev1 > source->rev2)
+ if (source->loc1.rev > source->loc2.rev)
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
for (j = 0; j < child->remaining_ranges->nelts; j++)
@@ -4476,7 +4484,7 @@ populate_remaining_ranges(apr_array_head
result_pool));
}
- if (source->rev1 > source->rev2) /* Reverse merge */
+ if (source->loc1.rev > source->loc2.rev) /* Reverse merge */
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
}
}
@@ -4877,11 +4885,11 @@ drive_merge_report_editor(const char *ta
svn_revnum_t target_start;
svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
const char *old_sess1_url, *old_sess2_url;
- svn_boolean_t is_rollback = source->rev1 > source->rev2;
+ svn_boolean_t is_rollback = source->loc1.rev > source->loc2.rev;
/* Start with a safe default starting revision for the editor and the
merge target. */
- target_start = source->rev1;
+ target_start = source->loc1.rev;
/* If we are honoring mergeinfo the starting revision for the merge target
might not be SOURCE->rev1, in fact the merge target might not need *any*
@@ -4907,7 +4915,7 @@ drive_merge_report_editor(const char *ta
if (child->remaining_ranges->nelts == 0)
{
/* The merge target doesn't need anything merged. */
- target_start = source->rev2;
+ target_start = source->loc2.rev;
}
else
{
@@ -4918,11 +4926,11 @@ drive_merge_report_editor(const char *ta
svn_merge_range_t *range =
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
- if ((!is_rollback && range->start > source->rev2)
- || (is_rollback && range->start < source->rev2))
+ if ((!is_rollback && range->start > source->loc2.rev)
+ || (is_rollback && range->start < source->loc2.rev))
{
/* Merge target's first remaining range doesn't intersect. */
- target_start = source->rev2;
+ target_start = source->loc2.rev;
}
else
{
@@ -4935,18 +4943,18 @@ drive_merge_report_editor(const char *ta
SVN_ERR(svn_client__ensure_ra_session_url(&old_sess1_url,
merge_b->ra_session1,
- source->url1, scratch_pool));
+ source->loc1.url, scratch_pool));
/* Temporarily point our second RA session to SOURCE->url1, too. We use
this to request individual file contents. */
SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
merge_b->ra_session2,
- source->url1, scratch_pool));
+ source->loc1.url, scratch_pool));
/* Get the diff editor and a reporter with which to, ultimately,
drive it. */
SVN_ERR(svn_client__get_diff_editor(&diff_editor, &diff_edit_baton,
depth,
- merge_b->ra_session2, source->rev1,
+ merge_b->ra_session2, source->loc1.rev,
FALSE /* walk_deleted_dirs */,
TRUE /* text_deltas */,
&merge_callbacks, merge_b,
@@ -4955,10 +4963,10 @@ drive_merge_report_editor(const char *ta
notification_receiver, notify_b,
scratch_pool));
SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
- &reporter, &report_baton, source->rev2,
+ &reporter, &report_baton, source->loc2.rev,
"", depth, merge_b->ignore_ancestry,
TRUE, /* text_deltas */
- source->url2, diff_editor, diff_edit_baton,
+ source->loc2.url, diff_editor, diff_edit_baton,
scratch_pool));
/* Drive the reporter. */
@@ -5003,8 +5011,8 @@ drive_merge_report_editor(const char *ta
{
range = APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
- if ((!is_rollback && range->start > source->rev2)
- || (is_rollback && range->start < source->rev2))
+ if ((!is_rollback && range->start > source->loc2.rev)
+ || (is_rollback && range->start < source->loc2.rev))
{
/* This child's first remaining range comes after the range
we are currently merging, so skip it. We expect to get
@@ -5042,14 +5050,14 @@ drive_merge_report_editor(const char *ta
SVN_ERR_ASSERT(child_repos_path);
if ((child->remaining_ranges->nelts == 0)
- || (is_rollback && (range->start < source->rev2))
- || (!is_rollback && (range->start > source->rev2)))
+ || (is_rollback && (range->start < source->loc2.rev))
+ || (!is_rollback && (range->start > source->loc2.rev)))
{
/* Nothing to merge to this child. We'll claim we have
it up to date so the server doesn't send us
anything. */
SVN_ERR(reporter->set_path(report_baton, child_repos_path,
- source->rev2, depth, FALSE,
+ source->loc2.rev, depth, FALSE,
NULL, iterpool));
}
else
@@ -6193,8 +6201,8 @@ static int
compare_merge_source_ts(const void *a,
const void *b)
{
- svn_revnum_t a_rev = ((const merge_source_t *)a)->rev1;
- svn_revnum_t b_rev = ((const merge_source_t *)b)->rev1;
+ svn_revnum_t a_rev = ((const merge_source_t *)a)->loc1.rev;
+ svn_revnum_t b_rev = ((const merge_source_t *)b)->loc1.rev;
if (a_rev == b_rev)
return 0;
return a_rev < b_rev ? 1 : -1;
@@ -6272,24 +6280,21 @@ combine_range_with_segments(apr_array_he
/* Build our merge source structure. */
merge_source = apr_pcalloc(pool, sizeof(*merge_source));
- merge_source->url1 = svn_path_url_add_component2(source_root_url,
- path1,
- pool);
- merge_source->url2 = svn_path_url_add_component2(source_root_url,
- segment->path,
- pool);
- merge_source->rev1 = rev1;
- merge_source->rev2 = MIN(segment->range_end, maxrev);
+ merge_source->loc1.url = svn_path_url_add_component2(source_root_url,
+ path1,
+ pool);
+ merge_source->loc2.url = svn_path_url_add_component2(source_root_url,
+ segment->path,
+ pool);
+ merge_source->loc1.rev = rev1;
+ merge_source->loc2.rev = MIN(segment->range_end, maxrev);
/* If this is subtractive, reverse the whole calculation. */
if (subtractive)
{
- svn_revnum_t tmprev = merge_source->rev1;
- const char *tmpurl = merge_source->url1;
- merge_source->rev1 = merge_source->rev2;
- merge_source->url1 = merge_source->url2;
- merge_source->rev2 = tmprev;
- merge_source->url2 = tmpurl;
+ repo_location_t tmploc = merge_source->loc1;
+ merge_source->loc1 = merge_source->loc2;
+ merge_source->loc2 = tmploc;
}
APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
@@ -6636,23 +6641,23 @@ subrange_source(const merge_source_t *so
svn_revnum_t end_rev,
apr_pool_t *pool)
{
- svn_boolean_t is_rollback = (source->rev1 > source->rev2);
- svn_boolean_t same_urls = (strcmp(source->url1, source->url2) == 0);
+ svn_boolean_t is_rollback = (source->loc1.rev > source->loc2.rev);
+ svn_boolean_t same_urls = (strcmp(source->loc1.url, source->loc2.url) == 0);
merge_source_t *real_source = apr_palloc(pool, sizeof(*source));
- real_source->url1 = source->url1;
- real_source->rev1 = start_rev;
- real_source->url2 = source->url2;
- real_source->rev2 = end_rev;
+ real_source->loc1.url = source->loc1.url;
+ real_source->loc1.rev = start_rev;
+ real_source->loc2.url = source->loc2.url;
+ real_source->loc2.rev = end_rev;
if (! same_urls)
{
- if (is_rollback && (end_rev != source->rev2))
+ if (is_rollback && (end_rev != source->loc2.rev))
{
- real_source->url2 = source->url1;
+ real_source->loc2.url = source->loc1.url;
}
- if ((! is_rollback) && (start_rev != source->rev1))
+ if ((! is_rollback) && (start_rev != source->loc1.rev))
{
- real_source->url1 = source->url2;
+ real_source->loc1.url = source->loc2.url;
}
}
return real_source;
@@ -6693,8 +6698,8 @@ do_file_merge(svn_mergeinfo_catalog_t re
svn_mergeinfo_t target_mergeinfo;
svn_merge_range_t *conflicted_range = NULL;
svn_boolean_t inherited = FALSE;
- svn_boolean_t is_rollback = (source->rev1 > source->rev2);
- const char *primary_url = is_rollback ? source->url1 : source->url2;
+ svn_boolean_t is_rollback = (source->loc1.rev > source->loc2.rev);
+ const char *primary_url = is_rollback ? source->loc1.url : source->loc2.url;
svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
svn_client__merge_path_t *merge_target = NULL;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
@@ -6704,8 +6709,8 @@ do_file_merge(svn_mergeinfo_catalog_t re
/* Note that this is a single-file merge. */
notify_b->is_single_file_merge = TRUE;
- range.start = source->rev1;
- range.end = source->rev2;
+ range.start = source->loc1.rev;
+ range.end = source->loc2.rev;
range.inheritable = TRUE;
if (honor_mergeinfo)
{
@@ -6718,8 +6723,8 @@ do_file_merge(svn_mergeinfo_catalog_t re
&(merge_target->implicit_mergeinfo),
&inherited, svn_mergeinfo_inherited,
merge_b->ra_session1, target_abspath,
- MAX(source->rev1, source->rev2),
- MIN(source->rev1, source->rev2),
+ MAX(source->loc1.rev, source->loc2.rev),
+ MIN(source->loc1.rev, source->loc2.rev),
ctx, scratch_pool, iterpool);
if (err)
@@ -6803,11 +6808,13 @@ do_file_merge(svn_mergeinfo_catalog_t re
real_source = subrange_source(source, r->start, r->end, iterpool);
SVN_ERR(single_file_merge_get_file(&tmpfile1, &props1,
merge_b->ra_session1,
- real_source->url1, real_source->rev1,
+ real_source->loc1.url,
+ real_source->loc1.rev,
target_abspath, iterpool));
SVN_ERR(single_file_merge_get_file(&tmpfile2, &props2,
merge_b->ra_session2,
- real_source->url2, real_source->rev2,
+ real_source->loc2.url,
+ real_source->loc2.rev,
target_abspath, iterpool));
/* Discover any svn:mime-type values in the proplists */
@@ -7156,7 +7163,8 @@ do_mergeinfo_unaware_dir_merge(const mer
svn_client__merge_path_t *item
= svn_client__merge_path_create(target_dir_wcpath, pool);
- item->remaining_ranges = svn_rangelist__initialize(source->rev1, source->rev2,
+ item->remaining_ranges = svn_rangelist__initialize(source->loc1.rev,
+ source->loc2.rev,
TRUE, pool);
APR_ARRAY_PUSH(notify_b->children_with_mergeinfo,
svn_client__merge_path_t *) = item;
@@ -7767,7 +7775,7 @@ record_mergeinfo_for_dir_merge(svn_merge
svn_mergeinfo_t subtree_history_as_mergeinfo;
apr_array_header_t *child_merge_src_rangelist;
const char *subtree_mergeinfo_url =
- svn_path_url_add_component2(merge_b->target->repos_root.url,
+ svn_path_url_add_component2(merge_b->target->loc.repo->url,
child_merge_src_fspath + 1,
iterpool);
@@ -7966,7 +7974,7 @@ record_mergeinfo_for_added_subtrees(
take the intersection of the naive mergeinfo with
MERGEINFO_PATH/rel_added_path's history. */
added_path_mergeinfo_url =
- svn_path_url_add_component2(merge_b->target->repos_root.url,
+ svn_path_url_add_component2(merge_b->target->loc.repo->url,
added_path_mergeinfo_fspath + 1,
iterpool);
SVN_ERR(svn_client__get_history_as_mergeinfo(
@@ -8228,7 +8236,7 @@ remove_noop_subtree_ranges(const merge_s
/* This function is only intended to work with forward merges. */
- if (source->rev1 > source->rev2)
+ if (source->loc1.rev > source->loc2.rev)
return SVN_NO_ERROR;
/* Another easy out: There are no subtrees. */
@@ -8241,8 +8249,10 @@ remove_noop_subtree_ranges(const merge_s
/* Given the requested merge of SOURCE->rev1:rev2 might there be any
part of this range required for subtrees but not for the target? */
- requested_ranges = svn_rangelist__initialize(MIN(source->rev1, source->rev2),
- MAX(source->rev1, source->rev2),
+ requested_ranges = svn_rangelist__initialize(MIN(source->loc1.rev,
+ source->loc2.rev),
+ MAX(source->loc1.rev,
+ source->loc2.rev),
TRUE, scratch_pool);
SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges,
root_child->remaining_ranges,
@@ -8296,11 +8306,12 @@ remove_noop_subtree_ranges(const merge_s
SVN_ERR(svn_client__path_relative_to_root(
&(log_gap_baton.target_fspath), merge_b->ctx->wc_ctx,
merge_b->target->abspath,
- merge_b->target->repos_root.url, TRUE, NULL,
+ merge_b->target->loc.repo->url, TRUE, NULL,
result_pool, scratch_pool));
SVN_ERR(svn_client__path_relative_to_root(
&(log_gap_baton.source_fspath), merge_b->ctx->wc_ctx,
- source->url2, merge_b->target->repos_root.url, TRUE, NULL,
+ source->loc2.url,
+ merge_b->target->loc.repo->url, TRUE, NULL,
result_pool, scratch_pool));
log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0,
sizeof(svn_revnum_t *));
@@ -8417,8 +8428,8 @@ do_directory_merge(svn_mergeinfo_catalog
svn_ra_session_t *ra_session;
svn_client__merge_path_t *target_merge_path;
- svn_boolean_t is_rollback = (source->rev1 > source->rev2);
- const char *primary_url = is_rollback ? source->url1 : source->url2;
+ svn_boolean_t is_rollback = (source->loc1.rev > source->loc2.rev);
+ const char *primary_url = is_rollback ? source->loc1.url : source->loc2.url;
svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
/* Note that this is not a single-file merge. */
@@ -8468,8 +8479,8 @@ do_directory_merge(svn_mergeinfo_catalog
/* Always start with a range which describes the most inclusive merge
possible, i.e. SOURCE->rev1:rev2. */
- range.start = source->rev1;
- range.end = source->rev2;
+ range.start = source->loc1.rev;
+ range.end = source->loc2.rev;
range.inheritable = TRUE;
if (honor_mergeinfo && !merge_b->reintegrate_merge)
@@ -8915,14 +8926,14 @@ do_merge(apr_hash_t **modified_subtrees,
/* Sanity check: if our left- and right-side merge sources are
the same, there's nothing to here. */
- if ((strcmp(source->url1, source->url2) == 0)
- && (source->rev1 == source->rev2))
+ if ((strcmp(source->loc1.url, source->loc2.url) == 0)
+ && (source->loc1.rev == source->loc2.rev))
continue;
/* Establish RA sessions to our URLs, reuse where possible. */
- SVN_ERR(ensure_ra_session_url(&ra_session1, source->url1,
+ SVN_ERR(ensure_ra_session_url(&ra_session1, source->loc1.url,
ctx, scratch_pool));
- SVN_ERR(ensure_ra_session_url(&ra_session2, source->url2,
+ SVN_ERR(ensure_ra_session_url(&ra_session2, source->loc2.url,
ctx, scratch_pool));
/* Populate the portions of the merge context baton that need to
@@ -9059,13 +9070,15 @@ merge_cousins_and_supplement_mergeinfo(c
SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
SVN_ERR(normalize_merge_sources_internal(
- &remove_sources, source->url1, source->rev1,
- svn_rangelist__initialize(source->rev1, yc_rev, TRUE, scratch_pool),
+ &remove_sources, source->loc1.url, source->loc1.rev,
+ svn_rangelist__initialize(source->loc1.rev, yc_rev, TRUE,
+ scratch_pool),
URL1_ra_session, ctx, scratch_pool, subpool));
SVN_ERR(normalize_merge_sources_internal(
- &add_sources, source->url2, source->rev2,
- svn_rangelist__initialize(yc_rev, source->rev2, TRUE, scratch_pool),
+ &add_sources, source->loc2.url, source->loc2.rev,
+ svn_rangelist__initialize(yc_rev, source->loc2.rev, TRUE,
+ scratch_pool),
URL2_ra_session, ctx, scratch_pool, subpool));
/* If this isn't a record-only merge, we'll first do a stupid
@@ -9250,45 +9263,82 @@ ensure_wc_path_has_repo_revision(const c
return SVN_NO_ERROR;
}
-/* Set *TARGET to a new, fully initialized, target description structure. */
+/* "Open" the target WC for a merge. That means:
+ * - find out its node kind
+ * - find out its exact repository location
+ * - check the WC for suitability (throw an error if unsuitable)
+ *
+ * Set *TARGET_P to a new, fully initialized, target description structure.
+ *
+ * ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, ALLOW_SWITCHED_SUBTREES determine
+ * whether the WC is deemed suitable; see ensure_wc_is_suitable_merge_target()
+ * for details.
+ *
+ * If the node is locally added, the rev and URL will be null/invalid. Some
+ * kinds of merge can use such a target; others can't.
+ */
static svn_error_t *
-target_node_location(merge_target_t **target_p,
- const char *wc_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+open_target_wc(merge_target_t **target_p,
+ const char *wc_abspath,
+ svn_boolean_t allow_mixed_rev,
+ svn_boolean_t allow_local_mods,
+ svn_boolean_t allow_switched_subtrees,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
merge_target_t *target = apr_palloc(result_pool, sizeof(*target));
- const char *relpath;
- target->abspath = wc_abspath;
+ target->abspath = apr_pstrdup(result_pool, wc_abspath);
+
SVN_ERR(svn_wc_read_kind(&target->kind, ctx->wc_ctx, wc_abspath, FALSE,
scratch_pool));
- SVN_ERR(svn_wc__node_get_origin(NULL /* is_copy */,
- &target->rev, &relpath,
- &target->repos_root.url,
- &target->repos_root.uuid,
- NULL, ctx->wc_ctx, wc_abspath,
- FALSE /* scan_deleted */,
- result_pool, scratch_pool));
- if (target->repos_root.url && relpath)
- {
- target->url = svn_path_url_add_component2(target->repos_root.url,
- relpath, result_pool);
- }
- else
- {
- target->url = NULL;
- SVN_ERR(svn_client_get_repos_root(&target->repos_root.url,
- &target->repos_root.uuid,
- wc_abspath,
- ctx, result_pool, scratch_pool));
- }
+
+ target->loc.repo = apr_palloc(result_pool, sizeof(*target->loc.repo));
+ SVN_ERR(svn_client__wc_node_get_origin(&target->loc.repo->url,
+ &target->loc.repo->uuid,
+ &target->loc.rev, &target->loc.url,
+ wc_abspath, ctx,
+ result_pool, scratch_pool));
+
+ SVN_ERR(ensure_wc_is_suitable_merge_target(
+ wc_abspath, ctx,
+ allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
+ scratch_pool));
*target_p = target;
return SVN_NO_ERROR;
}
+/* Open an RA session to PATH_OR_URL at PEG_REVISION. Set *RA_SESSION_P to
+ * the session and set *LOCATION_P to the resolved revision, URL and
+ * repository root. Allocate the results in RESULT_POOL. */
+static svn_error_t *
+open_source_session(repo_location_t **location_p,
+ svn_ra_session_t **ra_session_p,
+ const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ repo_location_t *location = apr_palloc(result_pool, sizeof(*location));
+ svn_ra_session_t *ra_session;
+
+ SVN_ERR(svn_client__ra_session_from_path(
+ &ra_session, &location->rev, &location->url,
+ path_or_url, NULL, peg_revision, peg_revision,
+ ctx, result_pool));
+ location->repo = apr_palloc(result_pool, sizeof(*location->repo));
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &location->repo->url,
+ result_pool));
+ SVN_ERR(svn_ra_get_uuid2(ra_session, &location->repo->uuid, result_pool));
+
+ *location_p = location;
+ *ra_session_p = ra_session;
+ return SVN_NO_ERROR;
+}
+
/*-----------------------------------------------------------------------*/
@@ -9312,9 +9362,9 @@ merge_locked(const char *source1,
apr_pool_t *scratch_pool)
{
merge_target_t *target;
+ repo_location_t *source1_loc, *source2_loc;
merge_source_t source;
svn_boolean_t related = FALSE, ancestral = FALSE;
- url_uuid_t source_repos_root, source_repos_root2;
svn_ra_session_t *ra_session1, *ra_session2;
apr_array_header_t *merge_sources;
svn_error_t *err;
@@ -9329,53 +9379,40 @@ merge_locked(const char *source1,
ancestor of the other -- just call svn_client_merge_peg3() with
the appropriate args. */
- SVN_ERR(target_node_location(&target, target_abspath,
- ctx, scratch_pool, scratch_pool));
-
- /* Do not allow merges into mixed-revision working copies. */
- SVN_ERR(ensure_wc_is_suitable_merge_target(target->abspath, ctx,
- allow_mixed_rev, TRUE, TRUE,
- scratch_pool));
+ SVN_ERR(open_target_wc(&target, target_abspath,
+ allow_mixed_rev, TRUE, TRUE,
+ ctx, scratch_pool, scratch_pool));
/* Open RA sessions to both sides of our merge source, and resolve URLs
* and revisions. */
sesspool = svn_pool_create(scratch_pool);
- SVN_ERR(svn_client__ra_session_from_path(&ra_session1,
- &source.rev1, &source.url1,
- source1, NULL, revision1, revision1,
- ctx, sesspool));
- SVN_ERR(svn_client__ra_session_from_path(&ra_session2,
- &source.rev2, &source.url2,
- source2, NULL, revision2, revision2,
- ctx, sesspool));
-
- SVN_ERR(svn_ra_get_repos_root2(ra_session1, &source_repos_root.url,
- sesspool));
- SVN_ERR(svn_ra_get_uuid2(ra_session1, &source_repos_root.uuid,
- scratch_pool));
- SVN_ERR(svn_ra_get_repos_root2(ra_session2, &source_repos_root2.url,
- sesspool));
- SVN_ERR(svn_ra_get_uuid2(ra_session2, &source_repos_root2.uuid,
- scratch_pool));
+ SVN_ERR(open_source_session(&source1_loc, &ra_session1, source1, revision1,
+ ctx, sesspool, scratch_pool));
+ source.loc1 = *source1_loc;
+ SVN_ERR(open_source_session(&source2_loc, &ra_session2, source2, revision2,
+ ctx, sesspool, scratch_pool));
+ source.loc2 = *source2_loc;
/* We can't do a diff between different repositories. */
/* ### We should also insist that the root URLs of the two sources match,
* as we are only carrying around a single source-repos-root from now
* on, and URL calculations will go wrong if they differ.
* Alternatively, teach the code to cope with differing root URLs. */
- SVN_ERR(check_same_repos(&source_repos_root, source.url1,
- &source_repos_root2, source.url2,
+ SVN_ERR(check_same_repos(source1_loc->repo, source.loc1.url,
+ source2_loc->repo, source.loc2.url,
FALSE /* strict_urls */, scratch_pool));
/* Do our working copy and sources come from the same repository? */
- same_repos = is_same_repos(&target->repos_root, &source_repos_root,
+ same_repos = is_same_repos(target->loc.repo, source1_loc->repo,
TRUE /* strict_urls */);
/* Unless we're ignoring ancestry, see if the two sources are related. */
if (! ignore_ancestry)
SVN_ERR(svn_client__get_youngest_common_ancestor(NULL, &yc_url, &yc_rev,
- source.url1, source.rev1,
- source.url2, source.rev2,
+ source.loc1.url,
+ source.loc1.rev,
+ source.loc2.url,
+ source.loc2.rev,
ctx, scratch_pool));
/* Check for a youngest common ancestor. If we have one, we'll be
@@ -9402,22 +9439,26 @@ merge_locked(const char *source1,
/* If the common ancestor matches the right side of our merge,
then we only need to reverse-merge the left side. */
- if ((strcmp(yc_url, source.url2) == 0) && (yc_rev == source.rev2))
+ if ((strcmp(yc_url, source.loc2.url) == 0)
+ && (yc_rev == source.loc2.rev))
{
ancestral = TRUE;
SVN_ERR(normalize_merge_sources_internal(
- &merge_sources, source.url1, source.rev1,
- svn_rangelist__initialize(source.rev1, yc_rev, TRUE, scratch_pool),
+ &merge_sources, source.loc1.url, source.loc1.rev,
+ svn_rangelist__initialize(source.loc1.rev, yc_rev, TRUE,
+ scratch_pool),
ra_session1, ctx, scratch_pool, scratch_pool));
}
/* If the common ancestor matches the left side of our merge,
then we only need to merge the right side. */
- else if ((strcmp(yc_url, source.url1) == 0) && (yc_rev == source.rev1))
+ else if ((strcmp(yc_url, source.loc1.url) == 0)
+ && (yc_rev == source.loc1.rev))
{
ancestral = TRUE;
SVN_ERR(normalize_merge_sources_internal(
- &merge_sources, source.url2, source.rev2,
- svn_rangelist__initialize(yc_rev, source.rev2, TRUE, scratch_pool),
+ &merge_sources, source.loc2.url, source.loc2.rev,
+ svn_rangelist__initialize(yc_rev, source.loc2.rev, TRUE,
+ scratch_pool),
ra_session2, ctx, scratch_pool, scratch_pool));
}
/* And otherwise, we need to do both: reverse merge the left
@@ -9946,15 +9987,12 @@ find_unmerged_mergeinfo_subroutine(svn_m
reintegrate source and the reintegrate target.
SOURCE_REPOS_REL_PATH is the path of the reintegrate source relative to
- the root of the repository. TARGET_REPOS_REL_PATH is the path of the
- reintegrate target relative to the root of the repository.
+ the root of the repository.
- TARGET_REV is the working revision the entire WC tree rooted at
- TARGET_REPOS_REL_PATH is at. SOURCE_REV is the peg revision of the
- reintegrate source.
+ SOURCE_REV is the peg revision of the reintegrate source.
SOURCE_RA_SESSION is a session opened to the SOURCE_REPOS_REL_PATH
- and TARGET_RA_SESSION is open to TARGET_REPOS_REL_PATH.
+ and TARGET_RA_SESSION is open to TARGET->loc.url.
For each entry in TARGET_HISTORY_HASH check that the history it
represents is contained in either the explicit mergeinfo for the
@@ -9982,8 +10020,7 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
svn_mergeinfo_catalog_t source_catalog,
apr_hash_t *target_history_hash,
const char *source_repos_rel_path,
- const char *target_repos_rel_path,
- svn_revnum_t target_rev,
+ const merge_target_t *target,
svn_revnum_t source_rev,
svn_ra_session_t *source_ra_session,
svn_ra_session_t *target_ra_session,
@@ -9991,16 +10028,19 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
+ const char *target_repos_rel_path;
const char *source_session_url;
- const char *target_session_url;
apr_hash_index_t *hi;
svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool);
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ SVN_ERR(svn_client__path_relative_to_root(&target_repos_rel_path,
+ ctx->wc_ctx, target->abspath,
+ NULL, FALSE, NULL,
+ scratch_pool, scratch_pool));
+
*youngest_merged_rev = SVN_INVALID_REVNUM;
- SVN_ERR(svn_ra_get_session_url(target_ra_session, &target_session_url,
- scratch_pool));
SVN_ERR(svn_ra_get_session_url(source_ra_session, &source_session_url,
scratch_pool));
@@ -10124,12 +10164,12 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
source_url = svn_path_url_add_component2(source_session_url,
path_rel_to_session, iterpool);
- target_url = svn_path_url_add_component2(target_session_url,
+ target_url = svn_path_url_add_component2(target->loc.url,
path_rel_to_session, iterpool);
err = svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
NULL /* has_rev_zero_history */,
target_url,
- target_rev, target_rev,
+ target->loc.rev, target->loc.rev,
SVN_INVALID_REVNUM,
target_ra_session,
ctx, iterpool);
@@ -10161,7 +10201,7 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
/* ### Why looking at SOURCE_url at TARGET_rev? */
SVN_ERR(find_unmerged_mergeinfo_subroutine(
&filtered_mergeinfo, target_history_as_mergeinfo,
- source_mergeinfo, source_url, target_rev,
+ source_mergeinfo, source_url, target->loc.rev,
source_ra_session, ctx, scratch_pool, iterpool));
if (apr_hash_count(filtered_mergeinfo))
apr_hash_set(new_catalog,
@@ -10194,30 +10234,26 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
merge actually performs. If no merge should be performed, set
*URL_LEFT to NULL and *REV_LEFT to SVN_INVALID_REVNUM.
- TARGET_ABSPATH is the absolute working copy path of the reintegrate
+ TARGET->abspath is the absolute working copy path of the reintegrate
merge.
- TARGET_REPOS_REL_PATH is the path of TARGET_ABSPATH relative to
- the root of the repository. SOURCE_REPOS_REL_PATH is the path of the
- reintegrate source relative to the root of the repository.
+ SOURCE_LOC is the reintegrate source.
SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped
to (svn_mergeinfo_t *) mergeinfo values for each working copy path with
- explicit mergeinfo in TARGET_ABSPATH. Actually we only need to know the
+ explicit mergeinfo in TARGET->abspath. Actually we only need to know the
paths, not the mergeinfo.
- TARGET_REV is the working revision the entire WC tree rooted at
- TARGET_REPOS_REL_PATH is at. SOURCE_REV is the peg revision of the
- reintegrate source.
+ TARGET->loc.rev is the working revision the entire WC tree rooted at
+ TARGET is at.
Populate *UNMERGED_TO_SOURCE_CATALOG with the mergeinfo describing what
- parts of TARGET_REPOS_REL_PATH@TARGET_REV have not been merged to
- SOURCE_REPOS_REL_PATH@SOURCE_REV, up to the youngest revision ever merged
- from the TARGET_ABSPATH to the source if such exists, see doc string for
- find_unmerged_mergeinfo().
+ parts of TARGET->loc have not been merged to SOURCE_LOC, up to the
+ youngest revision ever merged from the TARGET->abspath to the source if
+ such exists, see doc string for find_unmerged_mergeinfo().
- SOURCE_RA_SESSION is a session opened to the SOURCE_REPOS_REL_PATH
- and TARGET_RA_SESSION is open to TARGET_REPOS_REL_PATH.
+ SOURCE_RA_SESSION is a session opened to the SOURCE_LOC
+ and TARGET_RA_SESSION is open to TARGET->loc.url.
*URL_LEFT, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are
allocated in RESULT_POOL. SCRATCH_POOL is used for all temporary
@@ -10227,20 +10263,15 @@ calculate_left_hand_side(const char **ur
svn_revnum_t *rev_left,
svn_mergeinfo_t *merged_to_source_catalog,
svn_mergeinfo_t *unmerged_to_source_catalog,
- const char *target_abspath,
- const char *target_repos_rel_path,
+ const merge_target_t *target,
apr_hash_t *subtrees_with_mergeinfo,
- svn_revnum_t target_rev,
- const char *source_repos_rel_path,
- const char *source_repos_root,
- svn_revnum_t source_rev,
+ const repo_location_t *source_loc,
svn_ra_session_t *source_ra_session,
svn_ra_session_t *target_ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- const char *target_repos_root_url = source_repos_root; /* necessarily */
svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog;
apr_array_header_t *source_repos_rel_path_as_array
= apr_array_make(scratch_pool, 1, sizeof(const char *));
@@ -10251,8 +10282,7 @@ calculate_left_hand_side(const char **ur
svn_revnum_t youngest_merged_rev;
const char *yc_ancestor_url;
svn_revnum_t yc_ancestor_rev;
- const char *source_url;
- const char *target_url;
+ const char *source_repos_rel_path;
/* Initialize our return variables. */
*url_left = NULL;
@@ -10262,9 +10292,9 @@ calculate_left_hand_side(const char **ur
contained within SUBTREES_WITH_MERGEINFO. If this is the case then
add a dummy item for TARGET_ABSPATH so we get its history (i.e. implicit
mergeinfo) below. */
- if (!apr_hash_get(subtrees_with_mergeinfo, target_abspath,
+ if (!apr_hash_get(subtrees_with_mergeinfo, target->abspath,
APR_HASH_KEY_STRING))
- apr_hash_set(subtrees_with_mergeinfo, target_abspath,
+ apr_hash_set(subtrees_with_mergeinfo, target->abspath,
APR_HASH_KEY_STRING, apr_hash_make(result_pool));
/* Get the history segments (as mergeinfo) for TARGET_ABSPATH and any of
@@ -10287,12 +10317,12 @@ calculate_left_hand_side(const char **ur
NULL, FALSE,
NULL, scratch_pool,
iterpool));
- url = svn_path_url_add_component2(target_repos_root_url,
+ url = svn_path_url_add_component2(target->loc.repo->url,
path_rel_to_root, iterpool);
SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
NULL /* has_rev_zero_hist */,
url,
- target_rev, target_rev,
+ target->loc.rev, target->loc.rev,
SVN_INVALID_REVNUM,
target_ra_session,
ctx, scratch_pool));
@@ -10305,27 +10335,21 @@ calculate_left_hand_side(const char **ur
/* Check that SOURCE_URL@SOURCE_REV and TARGET_URL@TARGET_REV are
actually related, we can't reintegrate if they are not. Also
get an initial value for YC_ANCESTOR_REV. */
- source_url = svn_path_url_add_component2(source_repos_root,
- source_repos_rel_path,
- iterpool);
- target_url = svn_path_url_add_component2(source_repos_root,
- target_repos_rel_path,
- iterpool);
SVN_ERR(svn_client__get_youngest_common_ancestor(NULL, &yc_ancestor_url,
&yc_ancestor_rev,
- source_url, source_rev,
- target_url, target_rev,
+ source_loc->url, source_loc->rev,
+ target->loc.url, target->loc.rev,
ctx, iterpool));
if (!(yc_ancestor_url && SVN_IS_VALID_REVNUM(yc_ancestor_rev)))
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
- "'%s@%ld'"), source_url, source_rev,
- target_url, target_rev);
+ "'%s@%ld'"), source_loc->url, source_loc->rev,
+ target->loc.url, target->loc.rev);
/* If the source revision is the same as the youngest common
revision, then there can't possibly be any unmerged revisions
that we need to apply to target. */
- if (source_rev == yc_ancestor_rev)
+ if (source_loc->rev == yc_ancestor_rev)
{
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
@@ -10335,10 +10359,12 @@ calculate_left_hand_side(const char **ur
with differing explicit mergeinfo. */
APR_ARRAY_PUSH(source_repos_rel_path_as_array, const char *) = "";
SVN_ERR(svn_ra_get_mergeinfo(source_ra_session, &mergeinfo_catalog,
- source_repos_rel_path_as_array, source_rev,
+ source_repos_rel_path_as_array, source_loc->rev,
svn_mergeinfo_inherited,
TRUE, iterpool));
+ source_repos_rel_path = svn_uri_skip_ancestor(source_loc->repo->url,
+ source_loc->url, scratch_pool);
if (mergeinfo_catalog)
SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(&mergeinfo_catalog,
mergeinfo_catalog,
@@ -10360,9 +10386,8 @@ calculate_left_hand_side(const char **ur
mergeinfo_catalog,
target_history_hash,
source_repos_rel_path,
- target_repos_rel_path,
- target_rev,
- source_rev,
+ target,
+ source_loc->rev,
source_ra_session,
target_ra_session,
ctx,
@@ -10387,7 +10412,7 @@ calculate_left_hand_side(const char **ur
*URL_LEFT and *REV_LEFT to cover the youngest part of this range. */
*rev_left = youngest_merged_rev;
SVN_ERR(svn_client__repos_location(url_left, target_ra_session,
- target_url, target_rev,
+ target->loc.url, target->loc.rev,
youngest_merged_rev,
ctx, result_pool, iterpool));
}
@@ -10396,87 +10421,55 @@ calculate_left_hand_side(const char **ur
return SVN_NO_ERROR;
}
-/* The body of svn_client_find_reintegrate_merge(), which see for details. */
+/* Determine the URLs and revisions needed to perform a reintegrate merge
+ * from SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION into the working
+ * copy at TARGET.
+ *
+ * SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
+ * source and target branches respectively.
+ *
+ * Set *SOURCE_P to
+ * the source-left and source-right locations of the required merge. Set
+ * *YC_ANCESTOR_REV_P to the revision number of the youngest ancestor.
+ * Any of these output pointers may be NULL if not wanted.
+ *
+ * See svn_client_find_reintegrate_merge() for other details.
+ */
static svn_error_t *
-find_reintegrate_merge(svn_ra_session_t **target_ra_session_p,
- svn_ra_session_t **source_ra_session_p,
- merge_source_t **source_p,
+find_reintegrate_merge(merge_source_t **source_p,
svn_revnum_t *yc_ancestor_rev_p,
- const char *source_path_or_url,
- const svn_opt_revision_t *source_peg_revision,
- const char *target_abspath,
+ svn_ra_session_t *source_ra_session,
+ const repo_location_t *source_loc,
+ svn_ra_session_t *target_ra_session,
+ const merge_target_t *target,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- url_uuid_t source_repos_root;
- svn_ra_session_t *target_ra_session;
- svn_ra_session_t *source_ra_session;
- const char *source_repos_rel_path, *target_repos_rel_path;
const char *yc_ancestor_relpath;
svn_revnum_t yc_ancestor_rev;
- merge_target_t *target;
merge_source_t source;
svn_mergeinfo_t unmerged_to_source_mergeinfo_catalog;
svn_mergeinfo_t merged_to_source_mergeinfo_catalog;
svn_error_t *err;
apr_hash_t *subtrees_with_mergeinfo;
- /* Make sure we're dealing with a real URL. */
- SVN_ERR(svn_client_url_from_path2(&source.url2, source_path_or_url, ctx,
- scratch_pool, scratch_pool));
- if (! source.url2)
- return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
- _("'%s' has no URL"),
- svn_dirent_local_style(source_path_or_url,
- scratch_pool));
-
- SVN_ERR(target_node_location(&target, target_abspath,
- ctx, scratch_pool, scratch_pool));
-
- /* Determine the source's repository root URL. */
- SVN_ERR(svn_client_get_repos_root(&source_repos_root.url,
- &source_repos_root.uuid, source.url2,
- ctx, scratch_pool, scratch_pool));
-
- /* source_repos_root and target->repos_root are required to be the same,
- as mergeinfo doesn't come into play for cross-repository merging. */
- SVN_ERR(check_same_repos(&source_repos_root,
- svn_dirent_local_style(source_path_or_url,
- scratch_pool),
- &target->repos_root,
- svn_dirent_local_style(target_abspath, scratch_pool),
- TRUE /* strict_urls */, scratch_pool));
-
- /* A reintegrate merge requires the merge target to reflect a subtree
- * of the repository as found at a single revision. */
- SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
- FALSE, FALSE, FALSE,
- scratch_pool));
-
/* As the WC tree is "pure", use its last-updated-to revision as
the default revision for the left side of our merge, since that's
what the repository sub-tree is required to be up to date with
(with regard to the WC). */
- source.rev1 = target->rev;
-
- source_repos_rel_path = svn_uri_skip_ancestor(target->repos_root.url, source.url2,
- scratch_pool);
- SVN_ERR(svn_client__path_relative_to_root(&target_repos_rel_path,
- ctx->wc_ctx, target_abspath,
- NULL, FALSE, NULL,
- scratch_pool, scratch_pool));
+ /* ### Bogus/obsolete comment? */
/* Can't reintegrate to or from the root of the repository. */
- if (svn_path_is_empty(source_repos_rel_path)
- || svn_path_is_empty(target_repos_rel_path))
+ if (strcmp(source_loc->url, source_loc->repo->url) == 0
+ || strcmp(target->loc.url, target->loc.repo->url) == 0)
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Neither the reintegrate source nor target "
"can be the root of the repository"));
/* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
- target_abspath, svn_depth_infinity,
+ target->abspath, svn_depth_infinity,
ctx, scratch_pool, scratch_pool);
/* Issue #3896: If invalid mergeinfo in the reintegrate target
prevents us from proceeding, then raise the best error possible. */
@@ -10484,26 +10477,12 @@ find_reintegrate_merge(svn_ra_session_t
err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
SVN_ERR(err);
- /* Open two RA sessions, one to our source and one to our target. */
- SVN_ERR(svn_client__ra_session_from_path(&source_ra_session, &source.rev2, &source.url2,
- source.url2, NULL, source_peg_revision,
- source_peg_revision,
- ctx, result_pool));
- SVN_ERR(svn_client__open_ra_session_internal(&target_ra_session, NULL,
- target->url,
- NULL, NULL, FALSE, FALSE,
- ctx, scratch_pool));
-
- SVN_ERR(calculate_left_hand_side(&source.url1, &source.rev1,
+ SVN_ERR(calculate_left_hand_side(&source.loc1.url, &source.loc1.rev,
&merged_to_source_mergeinfo_catalog,
&unmerged_to_source_mergeinfo_catalog,
- target_abspath,
- target_repos_rel_path,
+ target,
subtrees_with_mergeinfo,
- source.rev1,
- source_repos_rel_path,
- source_repos_root.url,
- source.rev2,
+ source_loc,
source_ra_session,
target_ra_session,
ctx,
@@ -10511,12 +10490,8 @@ find_reintegrate_merge(svn_ra_session_t
/* Did calculate_left_hand_side() decide that there was no merge to
be performed here? */
- if (! source.url1)
+ if (! source.loc1.url)
{
- if (target_ra_session_p)
- *target_ra_session_p = NULL;
- if (source_ra_session_p)
- *source_ra_session_p = NULL;
if (source_p)
*source_p = NULL;
if (yc_ancestor_rev_p)
@@ -10524,31 +10499,38 @@ find_reintegrate_merge(svn_ra_session_t
return SVN_NO_ERROR;
}
+ source.loc2 = *source_loc;
+
/* If the target was moved after the source was branched from it,
it is possible that the left URL differs from the target's current
URL. If so, then adjust TARGET_RA_SESSION to point to the old URL. */
- if (strcmp(source.url1, target->url))
- SVN_ERR(svn_ra_reparent(target_ra_session, source.url1, scratch_pool));
+ if (strcmp(source.loc1.url, target->loc.url))
+ SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1.url, scratch_pool));
SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_ancestor_relpath, NULL,
&yc_ancestor_rev,
- source.url2, source.rev2,
- source.url1, source.rev1,
+ source.loc2.url,
+ source.loc2.rev,
+ source.loc1.url,
+ source.loc1.rev,
ctx, scratch_pool));
if (!(yc_ancestor_relpath && SVN_IS_VALID_REVNUM(yc_ancestor_rev)))
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
"'%s@%ld'"),
- source.url1, source.rev1,
- source.url2, source.rev2);
+ source.loc1.url, source.loc1.rev,
+ source.loc2.url, source.loc2.rev);
- if (source.rev1 > yc_ancestor_rev)
+ if (source.loc1.rev > yc_ancestor_rev)
{
/* Have we actually merged anything to the source from the
target? If so, make sure we've merged a contiguous
prefix. */
svn_mergeinfo_t final_unmerged_catalog = apr_hash_make(scratch_pool);
+ const char *source_repos_rel_path
+ = svn_uri_skip_ancestor(source_loc->repo->url, source_loc->url,
+ scratch_pool);
SVN_ERR(find_unsynced_ranges(source_repos_rel_path,
yc_ancestor_relpath,
@@ -10573,18 +10555,14 @@ find_reintegrate_merge(svn_ra_session_t
"previously merged from %s to the "
"reintegrate source, but this is "
"not the case:\n%s"),
- yc_ancestor_rev + 1, source.rev2,
- target->url,
+ yc_ancestor_rev + 1, source.loc2.rev,
+ target->loc.url,
source_mergeinfo_cat_string->data);
}
}
/* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
* Right side: branch@specified-peg-revision */
- if (target_ra_session_p)
- *target_ra_session_p = target_ra_session;
- if (source_ra_session_p)
- *source_ra_session_p = source_ra_session;
if (source_p)
*source_p = apr_pmemdup(result_pool, &source, sizeof(source));
if (yc_ancestor_rev_p)
@@ -10592,6 +10570,73 @@ find_reintegrate_merge(svn_ra_session_t
return SVN_NO_ERROR;
}
+/* Resolve the source and target locations and open RA sessions to them, and
+ * perform some checks appropriate for a reintegrate merge.
+ *
+ * Set *SOURCE_RA_SESSION_P and *SOURCE_LOC_P to a new session and the
+ * repository location of SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION. Set
+ * *TARGET_RA_SESSION_P and *TARGET_P to a new session and the repository
+ * location of the WC at TARGET_ABSPATH.
+ *
+ * Throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES error if the target WC node is
+ * a locally added node or if the source and target are not in the same
+ * repository. Throw a SVN_ERR_CLIENT_NOT_READY_TO_MERGE error if the
+ * target WC is not at a single revision without switched subtrees and
+ * without local mods.
+ *
+ * Allocate all the outputs in RESULT_POOL.
+ */
+static svn_error_t *
+open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
+ repo_location_t **source_loc_p,
+ svn_ra_session_t **target_ra_session_p,
+ merge_target_t **target_p,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_peg_revision,
+ const char *target_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ repo_location_t *source_loc;
+ merge_target_t *target;
+
+ /* Open the target WC. A reintegrate merge requires the merge target to
+ * reflect a subtree of the repository as found at a single revision. */
+ SVN_ERR(open_target_wc(&target, target_abspath,
+ FALSE, FALSE, FALSE,
+ ctx, scratch_pool, scratch_pool));
+ SVN_ERR(svn_client__open_ra_session_internal(target_ra_session_p, NULL,
+ target->loc.url,
+ NULL, NULL, FALSE, FALSE,
+ ctx, scratch_pool));
+ if (! target->loc.url)
+ return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("Can't reintegrate into '%s' because it is "
+ "locally added and therefore not related to "
+ "the merge source"),
+ svn_dirent_local_style(target->abspath,
+ scratch_pool));
+
+ SVN_ERR(open_source_session(&source_loc, source_ra_session_p,
+ source_path_or_url, source_peg_revision,
+ ctx, result_pool, scratch_pool));
+
+ /* source_loc->repo and target->loc.repo are required to be the same,
+ as mergeinfo doesn't come into play for cross-repository merging. */
+ SVN_ERR(check_same_repos(source_loc->repo,
+ svn_dirent_local_style(source_path_or_url,
+ scratch_pool),
+ target->loc.repo,
+ svn_dirent_local_style(target->abspath,
+ scratch_pool),
+ TRUE /* strict_urls */, scratch_pool));
+
+ *source_loc_p = source_loc;
+ *target_p = target;
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_client_find_reintegrate_merge(const char **url1_p,
svn_revnum_t *rev1_p,
@@ -10605,20 +10650,30 @@ svn_client_find_reintegrate_merge(const
apr_pool_t *scratch_pool)
{
const char *target_abspath;
+ svn_ra_session_t *source_ra_session;
+ repo_location_t *source_loc;
+ svn_ra_session_t *target_ra_session;
+ merge_target_t *target;
merge_source_t *source;
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath,
scratch_pool));
- SVN_ERR(find_reintegrate_merge(NULL, NULL, &source, NULL,
- source_path_or_url, source_peg_revision,
- target_abspath,
+
+ SVN_ERR(open_reintegrate_source_and_target(
+ &source_ra_session, &source_loc, &target_ra_session, &target,
+ source_path_or_url, source_peg_revision, target_abspath,
+ ctx, scratch_pool, scratch_pool));
+
+ SVN_ERR(find_reintegrate_merge(&source, NULL,
+ source_ra_session, source_loc,
+ target_ra_session, target,
ctx, result_pool, scratch_pool));
if (source)
{
- *url1_p = source->url1;
- *rev1_p = source->rev1;
- *url2_p = source->url2;
- *rev2_p = source->rev2;
+ *url1_p = source->loc1.url;
+ *rev1_p = source->loc1.rev;
+ *url2_p = source->loc2.url;
+ *rev2_p = source->loc2.rev;
}
else
{
@@ -10642,18 +10697,20 @@ merge_reintegrate_locked(const char *sou
{
svn_ra_session_t *target_ra_session, *source_ra_session;
merge_target_t *target;
+ repo_location_t *source_loc;
merge_source_t *source;
svn_revnum_t yc_ancestor_rev;
svn_boolean_t use_sleep;
svn_error_t *err;
- SVN_ERR(target_node_location(&target, target_abspath,
- ctx, scratch_pool, scratch_pool));
-
- SVN_ERR(find_reintegrate_merge(&target_ra_session, &source_ra_session,
- &source, &yc_ancestor_rev,
- source_path_or_url, source_peg_revision,
- target_abspath,
+ SVN_ERR(open_reintegrate_source_and_target(
+ &source_ra_session, &source_loc, &target_ra_session, &target,
+ source_path_or_url, source_peg_revision, target_abspath,
+ ctx, scratch_pool, scratch_pool));
+
+ SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor_rev,
+ source_ra_session, source_loc,
+ target_ra_session, target,
ctx, scratch_pool, scratch_pool));
if (! source)
@@ -10733,10 +10790,8 @@ merge_peg_locked(const char *source_path
apr_pool_t *scratch_pool)
{
merge_target_t *target;
- const char *source_url;
- svn_revnum_t source_peg_revnum;
+ repo_location_t *source_loc;
apr_array_header_t *merge_sources;
- url_uuid_t source_repos_root;
svn_ra_session_t *ra_session;
apr_pool_t *sesspool;
svn_boolean_t use_sleep = FALSE;
@@ -10745,30 +10800,24 @@ merge_peg_locked(const char *source_path
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
- SVN_ERR(target_node_location(&target, target_abspath,
- ctx, scratch_pool, scratch_pool));
- SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
- allow_mixed_rev, TRUE, TRUE,
- scratch_pool));
+ SVN_ERR(open_target_wc(&target, target_abspath,
+ allow_mixed_rev, TRUE, TRUE,
+ ctx, scratch_pool, scratch_pool));
/* Open an RA session to our source URL, and determine its root URL. */
sesspool = svn_pool_create(scratch_pool);
- SVN_ERR(svn_client__ra_session_from_path(
- &ra_session, &source_peg_revnum, &source_url,
- source_path_or_url, NULL, source_peg_revision, source_peg_revision,
- ctx, sesspool));
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_repos_root.url,
- scratch_pool));
- SVN_ERR(svn_ra_get_uuid2(ra_session, &source_repos_root.uuid, scratch_pool));
+ SVN_ERR(open_source_session(&source_loc, &ra_session,
+ source_path_or_url, source_peg_revision,
+ ctx, sesspool, scratch_pool));
/* Normalize our merge sources. */
SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url,
- source_url, source_peg_revnum,
+ source_loc->url, source_loc->rev,
ranges_to_merge, ra_session, ctx,
scratch_pool, scratch_pool));
/* Check for same_repos. */
- same_repos = is_same_repos(&target->repos_root, &source_repos_root,
+ same_repos = is_same_repos(target->loc.repo, source_loc->repo,
TRUE /* strict_urls */);
/* We're done with our little RA session. */
@@ -10831,3 +10880,421 @@ svn_client_merge_peg4(const char *source
return SVN_NO_ERROR;
}
+
+#ifdef SVN_WITH_SYMMETRIC_MERGE
+
+/* Details of a symmetric merge. */
+struct svn_client__symmetric_merge_t
+{
+ repo_location_t *yca, *base, *mid, *right;
+};
+
+/* */
+typedef struct source_and_target_t
+{
+ repo_location_t *source;
+ svn_ra_session_t *source_ra_session;
+ merge_target_t *target;
+ svn_ra_session_t *target_ra_session;
+} source_and_target_t;
+
+/* "Open" the source and target branches of a merge. That means:
+ * - find out their exact repository locations (resolve WC paths and
+ * non-numeric revision numbers),
+ * - check the branches are suitably related,
+ * - establish RA session(s) to the repo,
+ * - check the WC for suitability (throw an error if unsuitable)
+ *
+ * Record this information and return it in a new "merge context" object.
+ */
+static svn_error_t *
+open_source_and_target(source_and_target_t **source_and_target,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_peg_revision,
+ const char *target_abspath,
+ svn_boolean_t allow_mixed_rev,
+ svn_boolean_t allow_local_mods,
+ svn_boolean_t allow_switched_subtrees,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *session_pool,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
+
+ /* Target */
+ SVN_ERR(open_target_wc(&s_t->target, target_abspath,
+ allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
+ ctx, result_pool, scratch_pool));
+ SVN_ERR(svn_client_open_ra_session(&s_t->target_ra_session,
+ s_t->target->loc.url,
+ ctx, session_pool));
+
+ /* Source */
+ SVN_ERR(open_source_session(&s_t->source, &s_t->source_ra_session,
+ source_path_or_url, source_peg_revision,
+ ctx, result_pool, scratch_pool));
+
+ *source_and_target = s_t;
+ return SVN_NO_ERROR;
+}
+
+/* "Close" any resources that were acquired in the S_T structure. */
+static svn_error_t *
+close_source_and_target(source_and_target_t *s_t,
+ apr_pool_t *scratch_pool)
+{
+ /* close s_t->source_/target_ra_session */
+ return SVN_NO_ERROR;
+}
+
+/* Find a merge base location on the target branch, like in a sync
+ * merge.
+ *
+ * (Source-left) (Source-right = S_T->source)
+ * BASE RIGHT
+ * o-------o-----------o---
+ * / \ \
+ * -----o prev. \ \ this
+ * YCA \ merge \ \ merge
+ * o-----------o-----------o
+ * TARGET
+ *
+ */
+static svn_error_t *
+find_base_on_source(repo_location_t **base_p,
+ source_and_target_t *s_t,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_mergeinfo_t target_mergeinfo;
+ svn_client__merge_path_t *merge_target;
+ svn_boolean_t inherited;
+ merge_source_t source;
+ svn_merge_range_t *r;
+ repo_location_t *base;
+
+ merge_target = svn_client__merge_path_create(s_t->target->abspath,
+ scratch_pool);
+
+ /* Fetch target mergeinfo (all the way back to revision 1). */
+ SVN_ERR(get_full_mergeinfo(&target_mergeinfo,
+ &merge_target->implicit_mergeinfo,
+ &inherited, svn_mergeinfo_inherited,
+ s_t->target_ra_session, s_t->target->abspath,
+ s_t->source->rev, 1,
+ ctx, scratch_pool, scratch_pool));
+
+ /* In order to find the first unmerged change in the source, set
+ * MERGE_TARGET->remaining_ranges to the ranges left to merge,
+ * and look at the start revision of the first such range. */
+ source.loc1.url = s_t->source->url; /* ### WRONG: need historical URL/REV */
+ source.loc1.rev = 1;
+ source.loc2 = *s_t->source;
+ SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
+ &source,
+ target_mergeinfo,
+ NULL /*merge_b->implicit_src_gap*/,
+ FALSE /*child_inherits_implicit*/,
+ s_t->source_ra_session,
+ ctx, scratch_pool, scratch_pool));
+
+ r = APR_ARRAY_IDX(merge_target->remaining_ranges, 0, svn_merge_range_t *);
+ base = apr_palloc(result_pool, sizeof(*base));
+ base->url = s_t->source->url; /* ### WRONG: need historical URL */
+ base->rev = r->start;
+
+ *base_p = base;
+ return SVN_NO_ERROR;
+}
+
+/* Find a merge base location on the target branch, like in a reintegrate
+ * merge.
+ *
+ * MID RIGHT
+ * o-----------o-------o---
+ * / prev. / \
+ * -----o merge / \ this
+ * YCA \ / \ merge
+ * o-------o---------------o
+ * BASE TARGET
+ *
+ * Set *BASE_P to the latest location on the history of S_T->target at
+ * which all revisions up to *BASE_P are recorded as merged into RIGHT
+ * (which is S_T->source).
+ *
+ * ### TODO: Set *MID_P to the first location on the history of
+ * S_T->source at which all revisions up to BASE_P are recorded as merged.
+ */
+static svn_error_t *
+find_base_on_target(repo_location_t **base_p,
+ repo_location_t **mid_p,
+ source_and_target_t *s_t,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ repo_location_t *base = apr_palloc(result_pool, sizeof(*base));
+ svn_mergeinfo_t unmerged_to_source_mergeinfo_catalog;
+ svn_mergeinfo_t merged_to_source_mergeinfo_catalog;
+ apr_hash_t *subtrees_with_mergeinfo;
+
+ /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
+ SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
+ s_t->target->abspath,
+ svn_depth_infinity,
+ ctx, scratch_pool, scratch_pool));
+
+ SVN_ERR(calculate_left_hand_side(&base->url, &base->rev,
+ &merged_to_source_mergeinfo_catalog,
[... 251 lines stripped ...]