Reject attempts to merge between unrelated branches, or where the source and
target are the same branch, or where the source and target are different
objects within their respective branches.  These checks are applied in 'svn
merge', and only on 'reintegrate' and simple 'sync' merges where no revision
range is specified.  They are also applied in the 'mergeinfo' command.

### Tests failing: merge_tests.py 6 12; merge_reintegrate_tests.py 5 9.

* subversion/include/private/svn_client_private.h,
  subversion/libsvn_client/ra.c
  (svn_client__resolve_path_at_revisions,
   svn_client__youngest_common_ancestor): New functions.

* subversion/libsvn_client/client.h
  (svn_client__get_youngest_common_ancestor): Cross-reference to
    svn_client__youngest_common_ancestor() and tweak the doc string
    formatting.

* subversion/svn/cl.h,
  subversion/svn/util.c
  (svn_cl__check_related_source_and_target): New function.

* subversion/svn/merge-cmd.c
  (svn_cl__merge): Check relatedness of source and target for 'reintegrate'
    and simple 'sync' merges.

* subversion/svn/mergeinfo-cmd.c
  (svn_cl__mergeinfo): Check relatedness of source and target.

* subversion/tests/cmdline/mergeinfo_tests.py
  (no_mergeinfo, mergeinfo): Adjust to avoid specifying the same source and
    target, so as not to trigger the new checks.
--This line, and those below, will be ignored--

Index: subversion/include/private/svn_client_private.h
===================================================================
--- subversion/include/private/svn_client_private.h	(revision 1214156)
+++ subversion/include/private/svn_client_private.h	(working copy)
@@ -59,6 +59,37 @@
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool);
 
+/* */
+svn_error_t *
+svn_client__resolve_path_at_revision(const char **url_p,
+                                     svn_revnum_t *rev_p,
+                                     const char *path_or_url,
+                                     const svn_opt_revision_t *revision,
+                                     svn_client_ctx_t *ctx,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool);
+
+/* Set *ANCESTOR_URL and *ANCESTOR_REVISION to the URL and revision,
+ * respectively, of the youngest common ancestor of the two locations
+ * PATH_OR_URL1@REV1 and PATH_OR_URL2@REV2.
+ * This function assumes that PATH_OR_URL1@REV1 and PATH_OR_URL2@REV2
+ * both refer to the same repository.
+ *
+ * Use the authentication baton cached in CTX to authenticate against
+ * the repository.  Use POOL for all allocations.
+ *
+ * See also svn_client__get_youngest_common_ancestor().
+ */
+svn_error_t *
+svn_client__youngest_common_ancestor(const char **ancestor_url,
+                                     svn_revnum_t *ancestor_revision,
+                                     const char *path_or_url1,
+                                     const svn_opt_revision_t *revision1,
+                                     const char *path_or_url2,
+                                     const svn_opt_revision_t *revision2,
+                                     svn_client_ctx_t *ctx,
+                                     apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/libsvn_client/client.h
===================================================================
--- subversion/libsvn_client/client.h	(revision 1214156)
+++ subversion/libsvn_client/client.h	(working copy)
@@ -190,10 +190,14 @@
    no leading '/') and revision, respectively, of the two locations
    identified as URL1@REV1 and URL2@REV2.  Set *ANCESTOR_RELPATH to
    NULL and *ANCESTOR_REVISION to SVN_INVALID_REVNUM if they have no
-   common ancestor.  Use the authentication baton cached in CTX to
-   authenticate against the repository.  This function assumes that
-   URL1@REV1 and URL2@REV2 both refer to the same repository.  Use
-   POOL for all allocations. */
+   common ancestor.  This function assumes that URL1@REV1 and URL2@REV2
+   both refer to the same repository.
+
+   Use the authentication baton cached in CTX to authenticate against
+   the repository.  Use POOL for all allocations.
+
+   See also svn_client__youngest_common_ancestor().
+*/
 svn_error_t *
 svn_client__get_youngest_common_ancestor(const char **ancestor_relpath,
                                          svn_revnum_t *ancestor_revision,
Index: subversion/libsvn_client/ra.c
===================================================================
--- subversion/libsvn_client/ra.c	(revision 1214156)
+++ subversion/libsvn_client/ra.c	(working copy)
@@ -40,6 +40,7 @@
 
 #include "svn_private_config.h"
 #include "private/svn_wc_private.h"
+#include "private/svn_client_private.h"
 
 
 /* This is the baton that we pass svn_ra_open3(), and is associated with
@@ -488,6 +489,27 @@ svn_client__ra_session_from_path(svn_ra_
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_client__resolve_path_at_revision(const char **url_p,
+                                     svn_revnum_t *rev_p,
+                                     const char *path_or_url,
+                                     const svn_opt_revision_t *revision,
+                                     svn_client_ctx_t *ctx,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool)
+{
+  apr_pool_t *sesspool = svn_pool_create(scratch_pool);
+  svn_ra_session_t *session;
+
+  SVN_ERR(svn_client__ra_session_from_path(&session, rev_p, url_p,
+                                           path_or_url, NULL,
+                                           revision, revision,
+                                           ctx, sesspool));
+  *url_p = apr_pstrdup(result_pool, *url_p);
+  svn_pool_destroy(sesspool);
+  return SVN_NO_ERROR;
+}
+
 
 svn_error_t *
 svn_client__ensure_ra_session_url(const char **old_session_url,
@@ -882,3 +904,40 @@ svn_client__get_youngest_common_ancestor
   *ancestor_revision = yc_revision;
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_client__youngest_common_ancestor(const char **ancestor_url,
+                                     svn_revnum_t *ancestor_revision,
+                                     const char *path_or_url1,
+                                     const svn_opt_revision_t *revision1,
+                                     const char *path_or_url2,
+                                     const svn_opt_revision_t *revision2,
+                                     svn_client_ctx_t *ctx,
+                                     apr_pool_t *pool)
+{
+  svn_ra_session_t *session1, *session2;
+  const char *url1, *url2;
+  svn_revnum_t rev1, rev2;
+  const char *root_url, *ancestor_relpath;
+
+  SVN_ERR(svn_client__ra_session_from_path(&session1, &rev1, &url1,
+                                           path_or_url1, NULL,
+                                           revision1, revision1,
+                                           ctx, pool));
+  SVN_ERR(svn_client__ra_session_from_path(&session2, &rev2, &url2,
+                                           path_or_url2, NULL,
+                                           revision2, revision2,
+                                           ctx, pool));
+
+  SVN_ERR(svn_client__get_youngest_common_ancestor(
+            &ancestor_relpath, ancestor_revision,
+            url1, rev1, url2, rev2, ctx, pool));
+
+  SVN_ERR(svn_ra_get_repos_root2(session1, &root_url, pool));
+  if (ancestor_relpath)
+    *ancestor_url = svn_path_url_add_component2(root_url, ancestor_relpath,
+                                                pool);
+  else
+    *ancestor_url = NULL;
+  return SVN_NO_ERROR;
+}
Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h	(revision 1214156)
+++ subversion/svn/cl.h	(working copy)
@@ -822,6 +822,29 @@ svn_cl__check_related_source_and_target(const
                                   const char *path,
                                   apr_pool_t *pool);
 
+/* Check that PATH_OR_URL1@REVISION1 is related to but on a different line
+ * of history from PATH_OR_URL2@REVISION2.  Raise an error if not.
+ *
+ * If the two locations have a common ancestor and it's older than both of
+ * them, that's OK.  If the common ancestor is the same as one of the
+ * locations, then the 'branch' identified by that location is ambiguous --
+ * the user could mean a branch that continues to exist at the same URL in
+ * later revisions, but could also mean any branch that was branched from
+ * that location, including the other input to this function.  Therefore
+ * we reject this case.  Finally, if there is no common ancestor, then
+ * either the two locations are on unrelated branches or if they are on
+ * related branches then each points to a different object within its
+ * branch, perhaps having a parent-child or sibling relationship.  We
+ * reject this case.
+ */
+svn_error_t *
+svn_cl__check_related_source_and_target(const char *path_or_url1,
+                                        const svn_opt_revision_t *revision1,
+                                        const char *path_or_url2,
+                                        const svn_opt_revision_t *revision2,
+                                        svn_client_ctx_t *ctx,
+                                        apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/svn/merge-cmd.c
===================================================================
--- subversion/svn/merge-cmd.c	(revision 1214156)
+++ subversion/svn/merge-cmd.c	(working copy)
@@ -93,6 +93,7 @@ svn_cl__merge(apr_getopt_t *os,
   svn_opt_revision_t first_range_start, first_range_end, peg_revision1,
     peg_revision2;
   apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
+  svn_opt_revision_t unspecified = { svn_opt_revision_unspecified, { 0 } };
 
   /* Merge doesn't support specifying a revision or revision range
      when using --reintegrate. */
@@ -341,6 +342,10 @@ svn_cl__merge(apr_getopt_t *os,
 
   if (opt_state->reintegrate)
     {
+      SVN_ERR(svn_cl__check_related_source_and_target(
+                sourcepath1, &peg_revision1, targetpath, &unspecified,
+                ctx, pool));
+
       err = merge_reintegrate(sourcepath1, &peg_revision1, targetpath,
                               opt_state->dry_run, options, ctx, pool);
     }
@@ -358,6 +363,11 @@ svn_cl__merge(apr_getopt_t *os,
           range->start.value.number = 1;
           range->end = peg_revision1;
           APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = range;
+
+          /* And, since this must be a 'sync' merge, check relatedness. */
+          SVN_ERR(svn_cl__check_related_source_and_target(
+                    sourcepath1, &peg_revision1, targetpath, &unspecified,
+                    ctx, pool));
         }
 
       err = svn_client_merge_peg4(sourcepath1,
Index: subversion/svn/mergeinfo-cmd.c
===================================================================
--- subversion/svn/mergeinfo-cmd.c	(revision 1214156)
+++ subversion/svn/mergeinfo-cmd.c	(working copy)
@@ -114,6 +114,10 @@ svn_cl__mergeinfo(apr_getopt_t *os,
         tgt_peg_revision.kind = svn_opt_revision_base;
     }
 
+  SVN_ERR(svn_cl__check_related_source_and_target(source, &src_peg_revision,
+                                                  target, &tgt_peg_revision,
+                                                  ctx, pool));
+
   /* Do the real work, depending on the requested data flavor. */
   if (opt_state->show_revs == svn_cl__show_revs_merged)
     {
Index: subversion/svn/util.c
===================================================================
--- subversion/svn/util.c	(revision 1214156)
+++ subversion/svn/util.c	(working copy)
@@ -1423,3 +1423,68 @@ svn_cl__local_style_skip_ancestor(const
 
   return svn_dirent_local_style(relpath ? relpath : path, pool);
 }
+
+/* */
+static const char *
+path_for_display(const char *path_or_url,
+                 const svn_opt_revision_t *revision,
+                 apr_pool_t *pool)
+{
+  const char *rev_str = svn_opt__revision_to_string(revision, pool);
+
+  if (! svn_path_is_url(path_or_url))
+    path_or_url = svn_dirent_local_style(path_or_url, pool);
+  return apr_psprintf(pool, "%s@%s", path_or_url, rev_str);
+}
+
+svn_error_t *
+svn_cl__check_related_source_and_target(const char *path_or_url1,
+                                        const svn_opt_revision_t *revision1,
+                                        const char *path_or_url2,
+                                        const svn_opt_revision_t *revision2,
+                                        svn_client_ctx_t *ctx,
+                                        apr_pool_t *pool)
+{
+  const char *ancestor_url;
+  svn_revnum_t ancestor_rev;
+  svn_error_t *err = NULL;
+
+  SVN_ERR(svn_client__youngest_common_ancestor(
+            &ancestor_url, &ancestor_rev,
+            path_or_url1, revision1, path_or_url2, revision2,
+            ctx, pool));
+
+  if (ancestor_url)
+    {
+      const char *url1, *url2;
+      svn_revnum_t rev1, rev2;
+
+      SVN_DBG(("ancestor: %s@%ld\n", ancestor_url, ancestor_rev));
+
+      SVN_ERR(svn_client__resolve_path_at_revision(&url1, &rev1,
+                                                   path_or_url1, revision1,
+                                                   ctx, pool, pool));
+      SVN_ERR(svn_client__resolve_path_at_revision(&url2, &rev2,
+                                                   path_or_url2, revision2,
+                                                   ctx, pool, pool));
+      SVN_DBG(("url1: %s@%ld\n", url1, rev1));
+      SVN_DBG(("url2: %s@%ld\n", url2, rev2));
+      if (ancestor_rev == rev1 || ancestor_rev == rev2)
+        err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                _("Source and target are the same branch: "
+                                  "'%s@%ld' and '%s@%ld'"),
+                                url1, rev1, url2, rev2);
+    }
+  else
+    {
+      err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                              _("Source and target have no common ancestor: "
+                                "'%s' and '%s'"),
+                              path_for_display(path_or_url1, revision1, pool),
+                              path_for_display(path_or_url2, revision2, pool));
+    }
+  if (err)
+    err = svn_error_quick_wrap(err, _("This merge requires two different but "
+                                      "related branches"));
+  return err;
+}
Index: subversion/tests/cmdline/mergeinfo_tests.py
===================================================================
--- subversion/tests/cmdline/mergeinfo_tests.py	(revision 1214156)
+++ subversion/tests/cmdline/mergeinfo_tests.py	(working copy)
@@ -69,7 +69,8 @@ def no_mergeinfo(sbox):
 
   sbox.build(create_wc=False)
   svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
-                                           [], sbox.repo_url, sbox.repo_url)
+                                           [],
+                                           sbox.repo_url + '/A', sbox.repo_url)
 
 def mergeinfo(sbox):
   "'mergeinfo' on a path with mergeinfo"
@@ -79,9 +80,9 @@ def mergeinfo(sbox):
 
   # Dummy up some mergeinfo.
   svntest.actions.run_and_verify_svn(None, None, [], 'ps', SVN_PROP_MERGEINFO,
-                                     '/:1', wc_dir)
+                                     '/A:1', wc_dir)
   svntest.actions.run_and_verify_mergeinfo(adjust_error_for_server_version(""),
-                                           ['1'], sbox.repo_url, wc_dir)
+                                           ['1'], sbox.repo_url + '/A', wc_dir)
 
 @SkipUnless(server_has_mergeinfo)
 def explicit_mergeinfo_source(sbox):
