You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pb...@apache.org on 2012/03/27 01:23:49 UTC

svn commit: r1305667 [2/7] - in /subversion/branches/inheritable-props: ./ subversion/bindings/swig/python/svn/ subversion/bindings/swig/python/tests/ subversion/bindings/swig/ruby/svn/ subversion/bindings/swig/ruby/test/ subversion/include/private/ su...

Modified: subversion/branches/inheritable-props/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_client/merge.c?rev=1305667&r1=1305666&r2=1305667&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_client/merge.c Mon Mar 26 23:23:46 2012
@@ -177,12 +177,12 @@ typedef struct 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. */
+  const 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. */
+  const repo_location_t *loc2;
 
 } merge_source_t;
 
@@ -328,6 +328,136 @@ typedef struct merge_cmd_baton_t {
 
 /*** Utilities ***/
 
+/* Return a new repo_location_t structure, allocated in RESULT_POOL,
+ * initialized with deep copies of REPO, REV and URL. */
+static repo_location_t *
+repo_location_create(const url_uuid_t *repo,
+                     svn_revnum_t rev,
+                     const char *url,
+                     apr_pool_t *result_pool)
+{
+  repo_location_t *loc = apr_palloc(result_pool, sizeof(*loc));
+
+  /* ### This 'if' is because 'repo' currently can be null, at least in a
+   * repo_location_t inside a merge_source_t. */
+  if (repo)
+    {
+      loc->repo = apr_palloc(result_pool, sizeof(*loc->repo));
+      loc->repo->url = apr_pstrdup(result_pool, repo->url);
+      loc->repo->uuid = apr_pstrdup(result_pool, repo->uuid);
+    }
+  else
+    {
+      loc->repo = NULL;
+    }
+  loc->rev = rev;
+  loc->url = apr_pstrdup(result_pool, url);
+  return loc;
+}
+
+/* Return a deep copy of LOC, allocated in RESULT_POOL. */
+static repo_location_t *
+repo_location_dup(const repo_location_t *loc,
+                  apr_pool_t *result_pool)
+{
+  /* Allocate memory for the struct and its sub-struct together. */
+  repo_location_t *loc2 = apr_palloc(result_pool,
+                                     sizeof(*loc2) + sizeof(*loc2->repo));
+
+  /* ### This 'if' is because 'repo' currently can be null, at least in a
+   * repo_location_t inside a merge_source_t. */
+  if (loc->repo)
+    {
+      loc2->repo = (void *)((char *)loc2 + sizeof(*loc2));
+      loc2->repo->url = apr_pstrdup(result_pool, loc->repo->url);
+      loc2->repo->uuid = apr_pstrdup(result_pool, loc->repo->uuid);
+    }
+  else
+    {
+      loc2->repo = NULL;
+    }
+  loc2->rev = loc->rev;
+  loc2->url = apr_pstrdup(result_pool, loc->url);
+  return loc2;
+}
+
+/* Return a new merge_source_t structure, allocated in RESULT_POOL,
+ * initialized with deep copies of LOC1 and LOC2. */
+static merge_source_t *
+merge_source_create(const repo_location_t *loc1,
+                    const repo_location_t *loc2,
+                    apr_pool_t *result_pool)
+{
+  merge_source_t *s
+    = apr_palloc(result_pool, sizeof(*s));
+
+  s->loc1 = repo_location_dup(loc1, result_pool);
+  s->loc2 = repo_location_dup(loc2, result_pool);
+  return s;
+}
+
+/* Return a deep copy of SOURCE, allocated in RESULT_POOL. */
+static merge_source_t *
+merge_source_dup(const merge_source_t *source,
+                 apr_pool_t *result_pool)
+{
+  merge_source_t *s = apr_palloc(result_pool, sizeof(*s));
+
+  s->loc1 = repo_location_dup(source->loc1, result_pool);
+  s->loc2 = repo_location_dup(source->loc2, result_pool);
+  return s;
+}
+
+/* Like svn_client__repos_location() but using repo_location_t for input
+ * and output. */
+static svn_error_t *
+repos_location(repo_location_t **op_loc_p,
+               svn_ra_session_t *ra_session,
+               const repo_location_t *peg_loc,
+               svn_revnum_t op_revnum,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool)
+{
+  *op_loc_p = apr_palloc(result_pool, sizeof(**op_loc_p));
+  (*op_loc_p)->repo = peg_loc->repo;
+  (*op_loc_p)->rev = op_revnum;
+  SVN_ERR(svn_client__repos_location(&(*op_loc_p)->url, ra_session,
+                                     peg_loc->url, peg_loc->rev,
+                                     op_revnum,
+                                     ctx, result_pool, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+/* Set *ANCESTOR_P to the location of the youngest common ancestor of
+ * LOC1 and LOC2.  If the locations have no common ancestor, set
+ * *ANCESTOR_P to NULL.
+ *
+ * Like svn_client__get_youngest_common_ancestor() but using repo_location_t
+ * for input and output.
+ */
+static svn_error_t *
+get_youngest_common_ancestor(repo_location_t **ancestor_p,
+                             const repo_location_t *loc1,
+                             const repo_location_t *loc2,
+                             svn_client_ctx_t *ctx,
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool)
+{
+  const char *url;
+  svn_revnum_t rev;
+
+  SVN_ERR(svn_client__get_youngest_common_ancestor(
+            NULL, &url, &rev,
+            loc1->url, loc1->rev, loc2->url, loc2->rev,
+            ctx, result_pool));
+  if (url)
+    *ancestor_p = repo_location_create(loc1->repo, rev, url, result_pool);
+  else
+    *ancestor_p = NULL;
+  return SVN_NO_ERROR;
+}
+
 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
    of LOCAL_ABSPATH.  Use SCRATCH_POOL for temporary allocations. */
 static svn_error_t *
@@ -532,21 +662,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;
 }
@@ -863,8 +993,7 @@ filter_self_referential_mergeinfo(apr_ar
   int i;
   apr_pool_t *iterpool;
   svn_boolean_t is_added;
-  const char *target_base_url;
-  svn_revnum_t target_base_rev;
+  repo_location_t target_base;
 
   /* Issue #3383: We don't want mergeinfo from a foreign repos.
 
@@ -892,9 +1021,9 @@ filter_self_referential_mergeinfo(apr_ar
   if (is_added)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_client_url_from_path2(&target_base_url, target_abspath,
+  SVN_ERR(svn_client_url_from_path2(&target_base.url, target_abspath,
                                     ctx, pool, pool));
-  SVN_ERR(svn_wc__node_get_base_rev(&target_base_rev, ctx->wc_ctx,
+  SVN_ERR(svn_wc__node_get_base_rev(&target_base.rev, ctx->wc_ctx,
                                     target_abspath, pool));
 
   adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
@@ -965,7 +1094,7 @@ filter_self_referential_mergeinfo(apr_ar
          the cost of a roundtrip communication with the repository. */
       SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
                                           &mergeinfo,
-                                          target_base_rev,
+                                          target_base.rev,
                                           iterpool));
 
       /* Filter self-referential mergeinfo from younger_mergeinfo. */
@@ -994,7 +1123,7 @@ filter_self_referential_mergeinfo(apr_ar
               for (j = 0; j < rangelist->nelts; j++)
                 {
                   svn_error_t *err2;
-                  const char *start_url;
+                  repo_location_t *start_loc;
                   svn_merge_range_t *range =
                     APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
 
@@ -1006,10 +1135,10 @@ filter_self_referential_mergeinfo(apr_ar
                   /* Check if PATH@BASE_REVISION exists at
                      RANGE->START on the same line of history.
                      (start+1 because RANGE->start is not inclusive.) */
-                  err2 = svn_client__repos_location(&start_url, ra_session,
-                                                    target_base_url, target_base_rev,
-                                                    range->start + 1,
-                                                    ctx, iterpool, iterpool);
+                  err2 = repos_location(&start_loc, ra_session,
+                                        &target_base,
+                                        range->start + 1,
+                                        ctx, iterpool, iterpool);
                   if (err2)
                     {
                       if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
@@ -1054,7 +1183,7 @@ filter_self_referential_mergeinfo(apr_ar
                          PATH@RANGE->END actually belong to some other
                          line of history and we want to record this
                          mergeinfo, not filter it. */
-                      if (strcmp(start_url, merge_source_url) != 0)
+                      if (strcmp(start_loc->url, merge_source_url) != 0)
                         {
                           APR_ARRAY_PUSH(adjusted_rangelist,
                                          svn_merge_range_t *) = range;
@@ -1083,8 +1212,8 @@ filter_self_referential_mergeinfo(apr_ar
           svn_mergeinfo_t implicit_mergeinfo;
 
           SVN_ERR(svn_client__get_history_as_mergeinfo(
-            &implicit_mergeinfo, NULL, target_base_url,
-            target_base_rev, target_base_rev, SVN_INVALID_REVNUM,
+            &implicit_mergeinfo, NULL, target_base.url,
+            target_base.rev, target_base.rev, SVN_INVALID_REVNUM,
             ra_session, ctx, iterpool));
 
           /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
@@ -1176,7 +1305,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,
@@ -1804,7 +1933,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,
@@ -2199,7 +2328,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;
 
@@ -3361,7 +3490,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. */
@@ -3435,12 +3564,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,
@@ -3988,8 +4118,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;
 
@@ -4029,7 +4159,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));
@@ -4066,18 +4196,19 @@ 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. */
       svn_error_t *err;
-      const char *start_url;
+      repo_location_t *start_loc;
 
-      err = svn_client__repos_location(&start_url,
-                                       ra_session, source->url1, source->rev1,
-                                       child_base_revision,
-                                       ctx, scratch_pool, scratch_pool);
+      err = repos_location(&start_loc,
+                           ra_session,
+                           source->loc1,
+                           child_base_revision,
+                           ctx, scratch_pool, scratch_pool);
       if (err)
         {
           if (err->apr_err == SVN_ERR_FS_NOT_FOUND
@@ -4092,7 +4223,7 @@ calculate_remaining_ranges(svn_client__m
 
           SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath,
                                        scratch_pool, scratch_pool));
-          if (strcmp(start_url, url) == 0)
+          if (strcmp(start_loc->url, url) == 0)
             return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
                                     _("Cannot reverse-merge a range from a "
                                       "path's own future history; try "
@@ -4138,10 +4269,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;
 
@@ -4190,7 +4321,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;
@@ -4198,8 +4329,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 *));
@@ -4288,8 +4419,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));
             }
@@ -4310,13 +4443,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);
         }
@@ -4350,7 +4484,9 @@ populate_remaining_ranges(apr_array_head
   for (i = 0; i < children_with_mergeinfo->nelts; i++)
     {
       const char *child_repos_path;
-      merge_source_t child_source = *source;
+      repo_location_t loc1 = *source->loc1;
+      repo_location_t loc2 = *source->loc2;
+      merge_source_t child_source = { &loc1, &loc2 };
       svn_client__merge_path_t *child =
         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
       svn_client__merge_path_t *parent = NULL;
@@ -4368,10 +4504,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);
+      loc1.url = svn_path_url_add_component2(
+                                source->loc1->url, child_repos_path, iterpool);
+      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
@@ -4384,8 +4520,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. */
@@ -4436,7 +4572,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++)
@@ -4478,7 +4614,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));
         }
     }
@@ -4651,7 +4787,7 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
 
    IS_ROLLBACK is true if the caller is recording a reverse merge and false
    otherwise.  RANGELIST is the set of revisions being merged from
-   MERGEINFO_PATH to MERGE_B->TARGET_ABSPATH. */
+   MERGEINFO_PATH to MERGE_B->target. */
 static svn_error_t *
 record_skips(const char *mergeinfo_path,
              const apr_array_header_t *rangelist,
@@ -4770,7 +4906,7 @@ remove_absent_children(const char *targe
    false then for each path (if any) in MERGE_B->PATHS_WITH_DELETED_MERGEINFO
    remove that path from CHILDREN_WITH_MERGEINFO by setting that
    child to NULL.  The one exception is for the merge target itself,
-   MERGE_B->TARGET_ABSPATH, this must always be present in
+   MERGE_B->target->abspath, this must always be present in
    CHILDREN_WITH_MERGEINFO so this is never removed by this
    function. */
 static void
@@ -4879,11 +5015,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*
@@ -4909,7 +5045,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
         {
@@ -4920,11 +5056,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
             {
@@ -4937,18 +5073,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,
@@ -4957,10 +5093,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. */
@@ -5005,8 +5141,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
@@ -5044,14 +5180,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
@@ -5348,7 +5484,7 @@ insert_child_to_merge(apr_array_header_t
    get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for
    *CHILD.
 
-   If CHILD->ABSPATH is equal to MERGE_CMD_BATON->TARGET_ABSPATH do nothing.
+   If CHILD->ABSPATH is equal to MERGE_CMD_BATON->target->abspath do nothing.
    Else if CHILD->ABSPATH is switched or absent then make sure its immediate
    (as opposed to nearest) parent in CHILDREN_WITH_MERGEINFO is marked as
    missing a child.  If the immediate parent does not exist in
@@ -5615,14 +5751,14 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
 /* Helper for do_directory_merge() when performing merge-tracking aware
    merges.
 
-   Walk of the working copy tree rooted at MERGE_CMD_BATON->TARGET_ABSPATH to
+   Walk of the working copy tree rooted at MERGE_CMD_BATON->target->abspath to
    depth DEPTH.  Create an svn_client__merge_path_t * for any path which meets
    one or more of the following criteria:
 
      1) Path has working svn:mergeinfo.
      2) Path is switched.
      3) Path is a subtree of the merge target (i.e. is not equal to
-        MERGE_CMD_BATON->TARGET_ABSPATH) and has no mergeinfo of its own but
+        MERGE_CMD_BATON->target->abspath) and has no mergeinfo of its own but
         its immediate parent has mergeinfo with non-inheritable ranges.  If
         this isn't a dry-run and the merge is between differences in the same
         repository, then this function will set working mergeinfo on the path
@@ -5634,10 +5770,10 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
         sibling is switched, absent, scheduled for deletion, or missing due to
         a sparse checkout.
      6) Path is absent from disk due to an authz restriction.
-     7) Path is equal to MERGE_CMD_BATON->TARGET_ABSPATH.
+     7) Path is equal to MERGE_CMD_BATON->target->abspath.
      8) Path is an immediate *directory* child of
-        MERGE_CMD_BATON->TARGET_ABSPATH and DEPTH is svn_depth_immediates.
-     9) Path is an immediate *file* child of MERGE_CMD_BATON->TARGET_ABSPATH
+        MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
+     9) Path is an immediate *file* child of MERGE_CMD_BATON->target->abspath
         and DEPTH is svn_depth_files.
      10) Path is at a depth of 'empty' or 'files'.
      11) Path is missing from disk (e.g. due to an OS-level deletion).
@@ -5650,7 +5786,7 @@ get_wc_explicit_mergeinfo_catalog(apr_ha
    sorted by svn_path_compare_paths().  Set the remaining_ranges field of each
    element to NULL.
 
-   Note: Since the walk is rooted at MERGE_CMD_BATON->TARGET_ABSPATH, the
+   Note: Since the walk is rooted at MERGE_CMD_BATON->target->abspath, the
    latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the
    depth-first ordering it is guaranteed to be the first element in
    *CHILDREN_WITH_MERGEINFO.
@@ -5872,7 +6008,7 @@ get_mergeinfo_paths(apr_array_header_t *
         }
     }
 
-  /* Case 7: The merge target MERGE_CMD_BATON->TARGET_ABSPATH is always
+  /* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always
      present. */
   if (!get_child_with_mergeinfo(children_with_mergeinfo,
                                 merge_cmd_baton->target->abspath))
@@ -5885,10 +6021,10 @@ get_mergeinfo_paths(apr_array_header_t *
     }
 
   /* Case 8: Path is an immediate *directory* child of
-     MERGE_CMD_BATON->TARGET_ABSPATH and DEPTH is svn_depth_immediates.
+     MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
 
      Case 9: Path is an immediate *file* child of
-     MERGE_CMD_BATON->TARGET_ABSPATH and DEPTH is svn_depth_files. */
+     MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_files. */
   if (depth == svn_depth_immediates || depth == svn_depth_files)
     {
       int j;
@@ -6195,8 +6331,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;
@@ -6226,6 +6362,7 @@ combine_range_with_segments(apr_array_he
     {
       svn_location_segment_t *segment =
         APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
+      repo_location_t loc1 = { 0 }, loc2 = { 0 };
       merge_source_t *merge_source;
       const char *path1 = NULL;
       svn_revnum_t rev1;
@@ -6273,25 +6410,20 @@ combine_range_with_segments(apr_array_he
         continue;
 
       /* 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);
+      loc1.url = svn_path_url_add_component2(source_root_url, path1,
+                                             pool);
+      loc2.url = svn_path_url_add_component2(source_root_url, segment->path,
+                                             pool);
+      loc1.rev = rev1;
+      loc2.rev = MIN(segment->range_end, maxrev);
+      merge_source = merge_source_create(&loc1, &loc2, pool);
 
       /* 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;
+          const 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;
@@ -6311,19 +6443,19 @@ combine_range_with_segments(apr_array_he
  * no SOURCE_PATH_OR_URL argument;
  * MERGE_RANGE_TS (array of svn_merge_range_t *) instead of RANGES;
  * SOURCE_PEG_REVNUM instead of SOURCE_PEG_REVISION.
- * RA_SESSION is an RA session open to the repository of SOURCE_URL; it may
+ * RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may
  * be temporarily reparented within this function.
  */
 static svn_error_t *
 normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
-                                 const char *source_url,
-                                 svn_revnum_t source_peg_revnum,
+                                 const repo_location_t *source_loc,
                                  const apr_array_header_t *merge_range_ts,
                                  svn_ra_session_t *ra_session,
                                  svn_client_ctx_t *ctx,
                                  apr_pool_t *result_pool,
                                  apr_pool_t *scratch_pool)
 {
+  svn_revnum_t source_peg_revnum = source_loc->rev;
   svn_revnum_t oldest_requested, youngest_requested;
   svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
   const char *source_root_url;
@@ -6348,19 +6480,18 @@ normalize_merge_sources_internal(apr_arr
      all the underlying APIs would do in this case right now anyway). */
   if (source_peg_revnum < youngest_requested)
     {
-      const char *start_url;
+      repo_location_t *start_loc;
 
-      SVN_ERR(svn_client__repos_location(&start_url,
-                                         ra_session, source_url,
-                                         source_peg_revnum,
-                                         youngest_requested,
-                                         ctx, scratch_pool, scratch_pool));
+      SVN_ERR(repos_location(&start_loc,
+                             ra_session, source_loc,
+                             youngest_requested,
+                             ctx, scratch_pool, scratch_pool));
       source_peg_revnum = youngest_requested;
     }
 
   /* Fetch the locations for our merge range span. */
   SVN_ERR(svn_client__repos_location_segments(&segments,
-                                              ra_session, source_url,
+                                              ra_session, source_loc->url,
                                               source_peg_revnum,
                                               youngest_requested,
                                               oldest_requested,
@@ -6477,12 +6608,12 @@ normalize_merge_sources_internal(apr_arr
    requested merges; order the objects from oldest to youngest.
 
    Determine the requested merges by examining SOURCE_PATH_OR_URL (and its
-   associated URL and revision, SOURCE_URL and SOURCE_PEG_REVNUM) (which
+   associated URL and revision, SOURCE_LOC) (which
    specifies the line of history from which merges will be pulled) and
    RANGES_TO_MERGE (a list of svn_opt_revision_range_t's which provide
    revision ranges).
 
-   RA_SESSION is an RA session open to the repository of SOURCE_URL; it may
+   RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may
    be temporarily reparented within this function.  Use RA_SESSION to answer
    historical questions.
 
@@ -6497,8 +6628,7 @@ normalize_merge_sources_internal(apr_arr
 static svn_error_t *
 normalize_merge_sources(apr_array_header_t **merge_sources_p,
                         const char *source_path_or_url,
-                        const char *source_url,
-                        svn_revnum_t source_peg_revnum,
+                        const repo_location_t *source_loc,
                         const apr_array_header_t *ranges_to_merge,
                         svn_ra_session_t *ra_session,
                         svn_client_ctx_t *ctx,
@@ -6557,7 +6687,7 @@ normalize_merge_sources(apr_array_header
     }
 
   SVN_ERR(normalize_merge_sources_internal(
-            merge_sources_p, source_url, source_peg_revnum,
+            merge_sources_p, source_loc,
             merge_range_ts, ra_session, ctx, result_pool, scratch_pool));
 
   svn_pool_destroy(iterpool);
@@ -6638,26 +6768,25 @@ 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);
-  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;
+  svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
+  svn_boolean_t same_urls = (strcmp(source->loc1->url, source->loc2->url) == 0);
+  repo_location_t loc1 = *source->loc1;
+  repo_location_t loc2 = *source->loc2;
+
+  loc1.rev = start_rev;
+  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;
+          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;
+          loc1.url = source->loc2->url;
         }
     }
-  return real_source;
+  return merge_source_create(&loc1, &loc2, pool);
 }
 
 /* The single-file, simplified version of do_directory_merge(), which see for
@@ -6695,8 +6824,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);
@@ -6706,8 +6835,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)
     {
@@ -6720,8 +6849,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)
@@ -6742,6 +6871,9 @@ do_file_merge(svn_mergeinfo_catalog_t re
          by SOURCE->rev1:rev2. */
       if (!merge_b->record_only)
         {
+          /* ### Bug?  calculate_remaining_ranges() needs 'source' to adhere
+           *   to the requirements of 'MERGEINFO MERGE SOURCE NORMALIZATION'
+           *   here, but it doesn't appear to be guaranteed so. */
           SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
                                              source,
                                              target_mergeinfo,
@@ -6805,11 +6937,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 */
@@ -7158,7 +7292,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;
@@ -7634,7 +7769,7 @@ record_mergeinfo_for_dir_merge(svn_merge
 
   svn_merge_range_t range = *merged_range;
 
-  /* Regardless of what subtrees in MERGE_B->TARGET_ABSPATH might be missing
+  /* Regardless of what subtrees in MERGE_B->target->abspath might be missing
      could this merge have been operative? */
   operative_merge = subtree_touched_by_merge(merge_b->target->abspath,
                                              notify_b, iterpool);
@@ -7645,7 +7780,7 @@ record_mergeinfo_for_dir_merge(svn_merge
   if (!operative_merge)
     range.inheritable = TRUE;
 
-  /* Remove absent children at or under TARGET_WCPATH from
+  /* Remove absent children at or under MERGE_B->target->abspath from
      NOTIFY_B->CHILDREN_WITH_MERGEINFO
      before we calculate the merges performed. */
   remove_absent_children(merge_b->target->abspath,
@@ -7944,7 +8079,7 @@ record_mergeinfo_for_added_subtrees(
                         iterpool);
 
           /* Create the new mergeinfo path for added_path's mergeinfo.
-             (added_abspath had better be a child of target_abspath
+             (added_abspath had better be a child of MERGE_B->target->abspath
              or something is *really* wrong.) */
           rel_added_path = svn_dirent_is_child(merge_b->target->abspath,
                                                added_abspath, iterpool);
@@ -8008,7 +8143,7 @@ typedef struct log_noop_baton_t
      of this file.*/
   apr_array_header_t *children_with_mergeinfo;
 
-  /* Absolute repository path of MERGE_B->TARGET_ABSPATH. */
+  /* Absolute repository path of MERGE_B->target->abspath. */
   const char *target_fspath;
 
   /* Absolute repository path of younger of the two merge sources
@@ -8069,7 +8204,7 @@ rangelist_merge_revision(apr_array_heade
    Add LOG_ENTRY->REVISION to BATON->OPERATIVE_RANGES.
 
    If LOG_ENTRY->REVISION has already been fully merged to
-   MERGE_B->TARGET_ABSPATH per the mergeinfo in CHILDREN_WITH_MERGEINFO,
+   MERGE_B->target->abspath per the mergeinfo in CHILDREN_WITH_MERGEINFO,
    then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES.
 
    Use SCRATCH_POOL for temporary allocations.  Allocate additions to
@@ -8102,7 +8237,7 @@ log_noop_revs(void *baton,
 
   /* Examine each path affected by LOG_ENTRY->REVISION.  If the explicit or
      inherited mergeinfo for *all* of the corresponding paths under
-     MERGE_B->TARGET_ABSPATH reflects that LOG_ENTRY->REVISION has been
+     MERGE_B->target->abspath reflects that LOG_ENTRY->REVISION has been
      merged, then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. */
   for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
        hi;
@@ -8198,7 +8333,7 @@ log_noop_revs(void *baton,
 
    Find all the ranges required by subtrees in
    CHILDREN_WITH_MERGEINFO that are *not* required by
-   MERGE_B->TARGET_ABSPATH (i.e. CHILDREN_WITH_MERGEINFO[0]).  If such
+   MERGE_B->target->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]).  If such
    ranges exist, then find any subset of ranges which, if merged, would be
    inoperative.  Finally, if any inoperative ranges are found then remove
    these ranges from all of the subtree's REMAINING_RANGES.
@@ -8230,7 +8365,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. */
@@ -8243,8 +8378,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,
@@ -8302,7 +8439,8 @@ remove_noop_subtree_ranges(const merge_s
                     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->loc.repo->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 *));
@@ -8419,8 +8557,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. */
@@ -8470,8 +8608,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)
@@ -8917,14 +9055,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
@@ -9061,13 +9199,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,
+            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,
+            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
@@ -9352,14 +9492,12 @@ merge_locked(const char *source1,
 {
   merge_target_t *target;
   repo_location_t *source1_loc, *source2_loc;
-  merge_source_t source;
   svn_boolean_t related = FALSE, ancestral = FALSE;
   svn_ra_session_t *ra_session1, *ra_session2;
   apr_array_header_t *merge_sources;
   svn_error_t *err;
   svn_boolean_t use_sleep = FALSE;
-  const char *yc_url = NULL;
-  svn_revnum_t yc_rev = SVN_INVALID_REVNUM;
+  repo_location_t *yca = NULL;
   apr_pool_t *sesspool;
   svn_boolean_t same_repos;
 
@@ -9377,20 +9515,16 @@ merge_locked(const char *source1,
   sesspool = svn_pool_create(scratch_pool);
   SVN_ERR(open_source_session(&source1_loc, &ra_session1, source1, revision1,
                               ctx, sesspool, scratch_pool));
-  source.url1 = source1_loc->url;
-  source.rev1 = source1_loc->rev;
   SVN_ERR(open_source_session(&source2_loc, &ra_session2, source2, revision2,
                               ctx, sesspool, scratch_pool));
-  source.url2 = source2_loc->url;
-  source.rev2 = source2_loc->rev;
 
   /* 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(source1_loc->repo, source.url1,
-                           source2_loc->repo, source.url2,
+  SVN_ERR(check_same_repos(source1_loc->repo, source1_loc->url,
+                           source2_loc->repo, source2_loc->url,
                            FALSE /* strict_urls */, scratch_pool));
 
   /* Do our working copy and sources come from the same repository? */
@@ -9399,10 +9533,8 @@ merge_locked(const char *source1,
 
   /* 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,
-                                                     ctx, scratch_pool));
+    SVN_ERR(get_youngest_common_ancestor(&yca, source1_loc, source2_loc,
+                                         ctx, scratch_pool, scratch_pool));
 
   /* Check for a youngest common ancestor.  If we have one, we'll be
      doing merge tracking.
@@ -9421,40 +9553,46 @@ merge_locked(const char *source1,
                       merge recording, then record-only two merges:
                       from A to C, and from C to B
   */
-  if (yc_url && SVN_IS_VALID_REVNUM(yc_rev))
+  if (yca)
     {
       /* Note that our merge sources are related. */
       related = TRUE;
 
       /* 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(yca->url, source2_loc->url) == 0)
+          && (yca->rev == source2_loc->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, source1_loc,
+                    svn_rangelist__initialize(source1_loc->rev, yca->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(yca->url, source1_loc->url) == 0)
+               && (yca->rev == source1_loc->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, source2_loc,
+                    svn_rangelist__initialize(yca->rev, source2_loc->rev, TRUE,
+                                              scratch_pool),
                     ra_session2, ctx, scratch_pool, scratch_pool));
         }
       /* And otherwise, we need to do both: reverse merge the left
          side, and merge the right. */
       else
         {
+          merge_source_t source = { source1_loc, source2_loc };
+
           err = merge_cousins_and_supplement_mergeinfo(target,
                                                        ra_session1,
                                                        ra_session2,
                                                        &source,
-                                                       yc_rev,
+                                                       yca->rev,
                                                        same_repos,
                                                        depth,
                                                        ignore_ancestry, force,
@@ -9480,6 +9618,8 @@ merge_locked(const char *source1,
     }
   else
     {
+      merge_source_t source = { source1_loc, source2_loc };
+
       /* Build a single-item merge_source_t array. */
       merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
       APR_ARRAY_PUSH(merge_sources, merge_source_t *) = &source;
@@ -9778,24 +9918,24 @@ log_find_operative_revs(void *baton,
   return SVN_NO_ERROR;
 }
 
-/* Determine if the mergeinfo on a reintegrate source SOURCE_REPOS_REL_PATH,
+/* Determine if the mergeinfo on a reintegrate source SOURCE_LOC,
    reflects that the source is fully synced with the reintegrate target
-   TARGET_REPOS_REL_PATH, even if a naive interpretation of the source's
+   TARGET_LOC, even if a naive interpretation of the source's
    mergeinfo says otherwise -- See issue #3577.
 
    UNMERGED_CATALOG represents the history (as mergeinfo) from
-   TARGET_REPOS_REL_PATH that is not represented in SOURCE_REPOS_REL_PATH's
+   TARGET_LOC that is not represented in SOURCE_LOC's
    explicit/inherited mergeinfo as represented by MERGED_CATALOG.
    MERGEINFO_CATALOG may be empty if the source has no explicit or inherited
    mergeinfo.
 
-   Using RA_SESSION, which is pointed at TARGET_REPOS_REL_PATH, check that all
+   Using RA_SESSION, which is pointed at TARGET_LOC, check that all
    of the unmerged revisions in UNMERGED_CATALOG's mergeinfos are "phantoms",
    that is, one of the following conditions holds:
 
-     1) The revision affects no corresponding paths in SOURCE_REPOS_REL_PATH.
+     1) The revision affects no corresponding paths in SOURCE_LOC.
 
-     2) The revision affects corresponding paths in SOURCE_REPOS_REL_PATH,
+     2) The revision affects corresponding paths in SOURCE_LOC,
         but based on the mergeinfo in MERGED_CATALOG, the change was
         previously merged.
 
@@ -9807,8 +9947,8 @@ log_find_operative_revs(void *baton,
 
    Use SCRATCH_POOL for all temporary allocations. */
 static svn_error_t *
-find_unsynced_ranges(const char *source_repos_rel_path,
-                     const char *target_repos_rel_path,
+find_unsynced_ranges(const repo_location_t *source_loc,
+                     const repo_location_t *target_loc,
                      svn_mergeinfo_catalog_t unmerged_catalog,
                      svn_mergeinfo_catalog_t merged_catalog,
                      svn_mergeinfo_catalog_t true_unmerged_catalog,
@@ -9842,6 +9982,12 @@ find_unsynced_ranges(const char *source_
      are not yet merged to it. */
   if (potentially_unmerged_ranges)
     {
+      const char *source_repos_rel_path
+        = svn_uri_skip_ancestor(source_loc->repo->url, source_loc->url,
+                                scratch_pool);
+      const char *target_repos_rel_path
+        = svn_uri_skip_ancestor(target_loc->repo->url, target_loc->url,
+                                scratch_pool);
       svn_revnum_t oldest_rev =
         (APR_ARRAY_IDX(potentially_unmerged_ranges,
                        0,
@@ -10217,7 +10363,7 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
 /* Helper for svn_client_merge_reintegrate() which calculates the
    'left hand side' of the underlying two-URL merge that a --reintegrate
    merge actually performs.  If no merge should be performed, set
-   *URL_LEFT to NULL and *REV_LEFT to SVN_INVALID_REVNUM.
+   *LEFT_P to NULL.
 
    TARGET->abspath is the absolute working copy path of the reintegrate
    merge.
@@ -10240,12 +10386,11 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
    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
+   *LEFT_P, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are
    allocated in RESULT_POOL.  SCRATCH_POOL is used for all temporary
    allocations. */
 static svn_error_t *
-calculate_left_hand_side(const char **url_left,
-                         svn_revnum_t *rev_left,
+calculate_left_hand_side(repo_location_t **left_p,
                          svn_mergeinfo_t *merged_to_source_catalog,
                          svn_mergeinfo_t *unmerged_to_source_catalog,
                          const merge_target_t *target,
@@ -10265,24 +10410,22 @@ calculate_left_hand_side(const char **ur
   /* hash of paths mapped to arrays of svn_mergeinfo_t. */
   apr_hash_t *target_history_hash = apr_hash_make(scratch_pool);
   svn_revnum_t youngest_merged_rev;
-  const char *yc_ancestor_url;
-  svn_revnum_t yc_ancestor_rev;
+  repo_location_t *yc_ancestor;
   const char *source_repos_rel_path;
 
   /* Initialize our return variables. */
-  *url_left = NULL;
-  *rev_left = SVN_INVALID_REVNUM;
+  *left_p = NULL;
 
-  /* TARGET_ABSPATH may not have explicit mergeinfo and thus may not be
+  /* TARGET->abspath may not have explicit mergeinfo and thus may not be
      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
+     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,
                     APR_HASH_KEY_STRING))
     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
+  /* Get the history segments (as mergeinfo) for TARGET->abspath and any of
      its subtrees with explicit mergeinfo. */
   for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
        hi;
@@ -10317,15 +10460,12 @@ calculate_left_hand_side(const char **ur
                    APR_HASH_KEY_STRING, target_history_as_mergeinfo);
     }
 
-  /* Check that SOURCE_URL@SOURCE_REV and TARGET_URL@TARGET_REV are
+  /* Check that SOURCE_LOC and TARGET->loc are
      actually related, we can't reintegrate if they are not.  Also
-     get an initial value for YC_ANCESTOR_REV. */
-  SVN_ERR(svn_client__get_youngest_common_ancestor(NULL, &yc_ancestor_url,
-                                                   &yc_ancestor_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)))
+     get an initial value for the YCA revision number. */
+  SVN_ERR(get_youngest_common_ancestor(&yc_ancestor, source_loc, &target->loc,
+                                       ctx, iterpool, iterpool));
+  if (! yc_ancestor)
     return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
                              _("'%s@%ld' must be ancestrally related to "
                                "'%s@%ld'"), source_loc->url, source_loc->rev,
@@ -10334,7 +10474,7 @@ calculate_left_hand_side(const char **ur
   /* 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_loc->rev == yc_ancestor_rev)
+  if (source_loc->rev == yc_ancestor->rev)
     {
       svn_pool_destroy(iterpool);
       return SVN_NO_ERROR;
@@ -10367,7 +10507,7 @@ calculate_left_hand_side(const char **ur
      TARGET_REPOS_REL_PATH@TARGET_REV to SOURCE_REPOS_REL_PATH@SOURCE_REV. */
   SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog,
                                   &youngest_merged_rev,
-                                  yc_ancestor_rev,
+                                  yc_ancestor->rev,
                                   mergeinfo_catalog,
                                   target_history_hash,
                                   source_repos_rel_path,
@@ -10387,19 +10527,16 @@ calculate_left_hand_side(const char **ur
   if (youngest_merged_rev == SVN_INVALID_REVNUM)
     {
       /* We never merged to the source.  Just return the branch point. */
-      *url_left = apr_pstrdup(result_pool, yc_ancestor_url);
-      *rev_left = yc_ancestor_rev;
+      *left_p = repo_location_dup(yc_ancestor, result_pool);
     }
   else
     {
       /* We've previously merged some or all of the target, up to
-         youngest_merged_rev, from TARGET_ABSPATH to the source.  Set
-         *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->loc.url, target->loc.rev,
-                                         youngest_merged_rev,
-                                         ctx, result_pool, iterpool));
+         youngest_merged_rev, to the source.  Set
+         *LEFT_P to cover the youngest part of this range. */
+      SVN_ERR(repos_location(left_p, target_ra_session,
+                             &target->loc, youngest_merged_rev,
+                             ctx, result_pool, iterpool));
     }
 
   svn_pool_destroy(iterpool);
@@ -10415,14 +10552,14 @@ calculate_left_hand_side(const char **ur
  *
  * 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.
+ * *YC_ANCESTOR_P to the location 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(merge_source_t **source_p,
-                       svn_revnum_t *yc_ancestor_rev_p,
+                       repo_location_t **yc_ancestor_p,
                        svn_ra_session_t *source_ra_session,
                        const repo_location_t *source_loc,
                        svn_ra_session_t *target_ra_session,
@@ -10431,8 +10568,8 @@ find_reintegrate_merge(merge_source_t **
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool)
 {
-  const char *yc_ancestor_relpath;
-  svn_revnum_t yc_ancestor_rev;
+  repo_location_t *yc_ancestor;
+  repo_location_t *loc1;
   merge_source_t source;
   svn_mergeinfo_t unmerged_to_source_mergeinfo_catalog;
   svn_mergeinfo_t merged_to_source_mergeinfo_catalog;
@@ -10462,7 +10599,7 @@ find_reintegrate_merge(merge_source_t **
     err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
   SVN_ERR(err);
 
-  SVN_ERR(calculate_left_hand_side(&source.url1, &source.rev1,
+  SVN_ERR(calculate_left_hand_side(&loc1,
                                    &merged_to_source_mergeinfo_catalog,
                                    &unmerged_to_source_mergeinfo_catalog,
                                    target,
@@ -10471,53 +10608,46 @@ find_reintegrate_merge(merge_source_t **
                                    source_ra_session,
                                    target_ra_session,
                                    ctx,
-                                   result_pool, scratch_pool));
+                                   scratch_pool, scratch_pool));
 
   /* Did calculate_left_hand_side() decide that there was no merge to
      be performed here?  */
-  if (! source.url1)
+  if (! loc1)
     {
       if (source_p)
         *source_p = NULL;
-      if (yc_ancestor_rev_p)
-        *yc_ancestor_rev_p = SVN_INVALID_REVNUM;
+      if (yc_ancestor_p)
+        *yc_ancestor_p = NULL;
       return SVN_NO_ERROR;
     }
 
-  source.rev2 = source_loc->rev;
-  source.url2 = source_loc->url;
+  source.loc1 = loc1;
+  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->loc.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,
-                                                   ctx, scratch_pool));
+  SVN_ERR(get_youngest_common_ancestor(&yc_ancestor, source.loc2, source.loc1,
+                                       ctx, scratch_pool, scratch_pool));
 
-  if (!(yc_ancestor_relpath && SVN_IS_VALID_REVNUM(yc_ancestor_rev)))
+  if (! yc_ancestor)
     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,
+      SVN_ERR(find_unsynced_ranges(source_loc, yc_ancestor,
                                    unmerged_to_source_mergeinfo_catalog,
                                    merged_to_source_mergeinfo_catalog,
                                    final_unmerged_catalog,
@@ -10539,7 +10669,7 @@ find_reintegrate_merge(merge_source_t **
                                      "previously merged from %s to the "
                                      "reintegrate source, but this is "
                                      "not the case:\n%s"),
-                                   yc_ancestor_rev + 1, source.rev2,
+                                   yc_ancestor->rev + 1, source.loc2->rev,
                                    target->loc.url,
                                    source_mergeinfo_cat_string->data);
         }
@@ -10548,9 +10678,10 @@ find_reintegrate_merge(merge_source_t **
   /* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
    * Right side: branch@specified-peg-revision */
   if (source_p)
-    *source_p = apr_pmemdup(result_pool, &source, sizeof(source));
-  if (yc_ancestor_rev_p)
-    *yc_ancestor_rev_p = yc_ancestor_rev;
+    *source_p = merge_source_dup(&source, result_pool);
+
+  if (yc_ancestor_p)
+    *yc_ancestor_p = repo_location_dup(yc_ancestor, result_pool);
   return SVN_NO_ERROR;
 }
 
@@ -10654,10 +10785,10 @@ svn_client_find_reintegrate_merge(const 
                                  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
     {
@@ -10683,7 +10814,7 @@ merge_reintegrate_locked(const char *sou
   merge_target_t *target;
   repo_location_t *source_loc;
   merge_source_t *source;
-  svn_revnum_t yc_ancestor_rev;
+  repo_location_t *yc_ancestor;
   svn_boolean_t use_sleep;
   svn_error_t *err;
 
@@ -10692,7 +10823,7 @@ merge_reintegrate_locked(const char *sou
             source_path_or_url, source_peg_revision, target_abspath,
             ctx, scratch_pool, scratch_pool));
 
-  SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor_rev,
+  SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor,
                                  source_ra_session, source_loc,
                                  target_ra_session, target,
                                  ctx, scratch_pool, scratch_pool));
@@ -10710,7 +10841,7 @@ merge_reintegrate_locked(const char *sou
   err = merge_cousins_and_supplement_mergeinfo(target,
                                                target_ra_session,
                                                source_ra_session,
-                                               source, yc_ancestor_rev,
+                                               source, yc_ancestor->rev,
                                                TRUE /* same_repos */,
                                                svn_depth_infinity,
                                                FALSE /* ignore_ancestry */,
@@ -10796,7 +10927,7 @@ merge_peg_locked(const char *source_path
 
   /* Normalize our merge sources. */
   SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url,
-                                  source_loc->url, source_loc->rev,
+                                  source_loc,
                                   ranges_to_merge, ra_session, ctx,
                                   scratch_pool, scratch_pool));
 
@@ -10897,6 +11028,8 @@ open_source_and_target(source_and_target
                        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,
@@ -10906,7 +11039,7 @@ open_source_and_target(source_and_target
 
   /* Target */
   SVN_ERR(open_target_wc(&s_t->target, target_abspath,
-                         allow_mixed_rev, TRUE, TRUE,
+                         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,
@@ -10933,7 +11066,7 @@ close_source_and_target(source_and_targe
 /* Find a merge base location on the target branch, like in a sync
  * merge.
  *
- *          (Source-left) (Source-right)
+ *          (Source-left) (Source-right = S_T->source)
  *                BASE        RIGHT
  *          o-------o-----------o---
  *         /         \           \
@@ -10950,7 +11083,44 @@ find_base_on_source(repo_location_t **ba
                     apr_pool_t *result_pool,
                     apr_pool_t *scratch_pool)
 {
-  *base_p = NULL;
+  svn_mergeinfo_t target_mergeinfo;
+  svn_client__merge_path_t *merge_target;
+  svn_boolean_t inherited;
+  repo_location_t loc1 = { 0 };
+  merge_source_t source;
+  svn_merge_range_t *r;
+
+  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. */
+  loc1.url = s_t->source->url;  /* ### WRONG: need historical URL/REV */
+  loc1.rev = 1;
+  source.loc1 = &loc1;
+  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 *);
+
+  /* ### WRONG: need historical URL instead of s_t->source->url. */
+  *base_p = repo_location_create(s_t->source->repo, r->start, s_t->source->url,
+                                 result_pool);
   return SVN_NO_ERROR;
 }
 
@@ -10980,7 +11150,6 @@ find_base_on_target(repo_location_t **ba
                     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;
@@ -10991,7 +11160,7 @@ find_base_on_target(repo_location_t **ba
                                             svn_depth_infinity,
                                             ctx, scratch_pool, scratch_pool));
 
-  SVN_ERR(calculate_left_hand_side(&base->url, &base->rev,
+  SVN_ERR(calculate_left_hand_side(base_p,
                                    &merged_to_source_mergeinfo_catalog,
                                    &unmerged_to_source_mergeinfo_catalog,
                                    s_t->target,
@@ -11001,14 +11170,12 @@ find_base_on_target(repo_location_t **ba
                                    s_t->target_ra_session,
                                    ctx, result_pool, scratch_pool));
 
-  if (base->url)
+  if (*base_p)
     {
-      *base_p = base;
       *mid_p = s_t->source;  /* ### WRONG! This is quite difficult. */
     }
   else
     {
-      *base_p = NULL;
       *mid_p = NULL;
     }
 
@@ -11026,15 +11193,10 @@ find_symmetric_merge(repo_location_t **y
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 {
-  repo_location_t *yca, *base_on_source, *base_on_target, *mid;
+  repo_location_t *base_on_source, *base_on_target, *mid;
 
-  yca = apr_palloc(result_pool, sizeof(*yca));
-  SVN_ERR(svn_client__get_youngest_common_ancestor(
-            NULL, &yca->url, &yca->rev,
-            s_t->source->url, s_t->source->rev,
-            s_t->target->loc.url, s_t->target->loc.rev,
-            ctx, result_pool));
-  *yca_p = yca;
+  SVN_ERR(get_youngest_common_ancestor(yca_p, s_t->source, &s_t->target->loc,
+                                       ctx, result_pool, result_pool));
 
   /* Find the latest revision of A synced to B and the latest
    * revision of B synced to A.
@@ -11070,7 +11232,7 @@ find_symmetric_merge(repo_location_t **y
        * the base is the youngest common ancestor of the branches.  We'll
        * set MID=NULL; in theory the end result should be the same if we
        * set MID=YCA instead. */
-      *base_p = yca;
+      *base_p = *yca_p;
       *mid_p = NULL;
     }
 
@@ -11083,6 +11245,8 @@ svn_client__find_symmetric_merge(svn_cli
                                  const svn_opt_revision_t *source_revision,
                                  const char *target_wcpath,
                                  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)
@@ -11093,7 +11257,7 @@ svn_client__find_symmetric_merge(svn_cli
 
   SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath, scratch_pool));
   SVN_ERR(open_source_and_target(&s_t, source_path_or_url, source_revision,
-                                 target_abspath, allow_mixed_rev,
+                                 target_abspath, allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
                                  ctx, result_pool, result_pool, scratch_pool));
 
   /* Check source is in same repos as target. */
@@ -11152,17 +11316,15 @@ do_symmetric_merge_locked(const svn_clie
                           apr_pool_t *scratch_pool)
 {
   merge_target_t *target;
-  merge_source_t *source = apr_palloc(scratch_pool, sizeof(*source));
+  merge_source_t source;
   svn_boolean_t use_sleep = FALSE;
   svn_error_t *err;
 
   SVN_ERR(open_target_wc(&target, target_abspath, TRUE, TRUE, TRUE,
                          ctx, scratch_pool, scratch_pool));
 
-  source->url1 = merge->base->url;
-  source->rev1 = merge->base->rev;
-  source->url2 = merge->right->url;
-  source->rev2 = merge->right->rev;
+  source.loc1 = merge->base;
+  source.loc2 = merge->right;
   SVN_DBG(("yca   %s@%ld\n", merge->yca->url, merge->yca->rev));
   SVN_DBG(("base  %s@%ld\n", merge->base->url, merge->base->rev));
   if (merge->mid)
@@ -11173,12 +11335,12 @@ do_symmetric_merge_locked(const svn_clie
     {
       svn_ra_session_t *ra_session = NULL;
 
-      SVN_ERR(ensure_ra_session_url(&ra_session, source->url1,
+      SVN_ERR(ensure_ra_session_url(&ra_session, source.loc1->url,
                                     ctx, scratch_pool));
 
       err = merge_cousins_and_supplement_mergeinfo(target,
                                                    ra_session, ra_session,
-                                                   source, merge->yca->rev,
+                                                   &source, merge->yca->rev,
                                                    TRUE /* same_repos */,
                                                    depth, ignore_ancestry,
                                                    force, record_only,
@@ -11192,7 +11354,7 @@ do_symmetric_merge_locked(const svn_clie
       apr_array_header_t *merge_sources;
 
       merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
-      APR_ARRAY_PUSH(merge_sources, const merge_source_t *) = source;
+      APR_ARRAY_PUSH(merge_sources, const merge_source_t *) = &source;
 
       err = do_merge(NULL, NULL, merge_sources, target,
                      TRUE /*sources_ancestral*/, TRUE /*related*/,

Modified: subversion/branches/inheritable-props/subversion/libsvn_fs/fs-loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_fs/fs-loader.h?rev=1305667&r1=1305666&r2=1305667&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_fs/fs-loader.h (original)
+++ subversion/branches/inheritable-props/subversion/libsvn_fs/fs-loader.h Mon Mar 26 23:23:46 2012
@@ -336,6 +336,7 @@ typedef struct root_vtable_t
                         svn_fs_root_t *ancestor_root,
                         const char *ancestor_path,
                         apr_pool_t *pool);
+  /* Mergeinfo. */
   svn_error_t *(*get_mergeinfo)(svn_mergeinfo_catalog_t *catalog,
                                 svn_fs_root_t *root,
                                 const apr_array_header_t *paths,