You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2011/12/16 22:05:08 UTC

svn commit: r1215273 - in /subversion/trunk/subversion: include/private/svn_client_private.h libsvn_client/client.h libsvn_client/ra.c svn/cl.h svn/merge-cmd.c svn/mergeinfo-cmd.c svn/util.c

Author: julianfoad
Date: Fri Dec 16 21:05:07 2011
New Revision: 1215273

URL: http://svn.apache.org/viewvc?rev=1215273&view=rev
Log:
Reject attempts to merge between unrelated branches, or where the source and
target are different objects within their respective branches.  These checks
are applied in 'svn merge', only on 'reintegrate' and simple 'sync' merges
where no revision range is specified.  They are also applied in the
'svn mergeinfo' command.

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

* subversion/libsvn_client/client.h
  (svn_client__get_youngest_common_ancestor): Cross-reference to
    svn_client__youngest_common_ancestor().

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

* 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.

Modified:
    subversion/trunk/subversion/include/private/svn_client_private.h
    subversion/trunk/subversion/libsvn_client/client.h
    subversion/trunk/subversion/libsvn_client/ra.c
    subversion/trunk/subversion/svn/cl.h
    subversion/trunk/subversion/svn/merge-cmd.c
    subversion/trunk/subversion/svn/mergeinfo-cmd.c
    subversion/trunk/subversion/svn/util.c

Modified: subversion/trunk/subversion/include/private/svn_client_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_client_private.h?rev=1215273&r1=1215272&r2=1215273&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_client_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_client_private.h Fri Dec 16 21:05:07 2011
@@ -59,6 +59,29 @@ svn_client__create_status(svn_client_sta
                           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.  Set *ANCESTOR_RELPATH to
+ * NULL and *ANCESTOR_REVISION to SVN_INVALID_REVNUM if they have no
+ * common ancestor.  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.
+ *
+ * See also svn_client__get_youngest_common_ancestor().
+ */
+svn_error_t *
+svn_client__youngest_common_ancestor(const char **ancestor_url,
+                                     svn_revnum_t *ancestor_rev,
+                                     const char *path_or_url1,
+                                     const svn_opt_revision_t *revision1,
+                                     const char *path_or_url2,
+                                     const svn_opt_revision_t *revision2,
+                                     svn_client_ctx_t *ctx,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/trunk/subversion/libsvn_client/client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/client.h?rev=1215273&r1=1215272&r2=1215273&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/client.h (original)
+++ subversion/trunk/subversion/libsvn_client/client.h Fri Dec 16 21:05:07 2011
@@ -199,6 +199,8 @@ svn_client__repos_location_segments(apr_
 
    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,

Modified: subversion/trunk/subversion/libsvn_client/ra.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/ra.c?rev=1215273&r1=1215272&r2=1215273&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/ra.c (original)
+++ subversion/trunk/subversion/libsvn_client/ra.c Fri Dec 16 21:05:07 2011
@@ -897,3 +897,36 @@ 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_rev,
+                                     const char *path_or_url1,
+                                     const svn_opt_revision_t *revision1,
+                                     const char *path_or_url2,
+                                     const svn_opt_revision_t *revision2,
+                                     svn_client_ctx_t *ctx,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool)
+{
+  apr_pool_t *sesspool = svn_pool_create(scratch_pool);
+  svn_ra_session_t *session;
+  const char *url1, *url2;
+  svn_revnum_t rev1, rev2;
+
+  /* Resolve the two locations */
+  SVN_ERR(svn_client__ra_session_from_path(&session, &rev1, &url1,
+                                           path_or_url1, NULL,
+                                           revision1, revision1,
+                                           ctx, sesspool));
+  SVN_ERR(resolve_rev_and_url(&rev2, &url2, session,
+                              path_or_url2, revision2, revision2,
+                              ctx, scratch_pool));
+
+  SVN_ERR(svn_client__get_youngest_common_ancestor(
+            NULL, ancestor_url, ancestor_rev,
+            url1, rev1, url2, rev2, ctx, result_pool));
+
+  svn_pool_destroy(sesspool);
+  return SVN_NO_ERROR;
+}

Modified: subversion/trunk/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1215273&r1=1215272&r2=1215273&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/cl.h (original)
+++ subversion/trunk/subversion/svn/cl.h Fri Dec 16 21:05:07 2011
@@ -822,6 +822,23 @@ svn_cl__local_style_skip_ancestor(const 
                                   const char *path,
                                   apr_pool_t *pool);
 
+/* Check that PATH_OR_URL1@REVISION1 is related to PATH_OR_URL2@REVISION2.
+ * Raise an error if not.
+ *
+ * ### Ideally we would also check that they are on different lines of
+ * history.  That is easy in common cases, but to give a correct answer in
+ * general we need to know the operative revision(s) as well.  For example,
+ * when one location is the branch point from which the other branch was
+ * copied.
+ */
+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 */

Modified: subversion/trunk/subversion/svn/merge-cmd.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/merge-cmd.c?rev=1215273&r1=1215272&r2=1215273&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/merge-cmd.c (original)
+++ subversion/trunk/subversion/svn/merge-cmd.c Fri Dec 16 21:05:07 2011
@@ -112,6 +112,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. */
@@ -347,6 +348,11 @@ svn_cl__merge(apr_getopt_t *os,
 
   if (opt_state->reintegrate)
     {
+      SVN_ERR_W(svn_cl__check_related_source_and_target(
+                  sourcepath1, &peg_revision1, targetpath, &unspecified,
+                  ctx, pool),
+                _("Source and target must be different but related branches"));
+
       err = merge_reintegrate(sourcepath1, &peg_revision1, targetpath,
                               opt_state->dry_run, options, ctx, pool);
     }
@@ -364,6 +370,12 @@ 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;
+
+          /* This must be a 'sync' merge so check branch relationship. */
+          SVN_ERR_W(svn_cl__check_related_source_and_target(
+                      sourcepath1, &peg_revision1, targetpath, &unspecified,
+                      ctx, pool),
+                _("Source and target must be different but related branches"));
         }
 
       err = svn_client_merge_peg4(sourcepath1,

Modified: subversion/trunk/subversion/svn/mergeinfo-cmd.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/mergeinfo-cmd.c?rev=1215273&r1=1215272&r2=1215273&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/mergeinfo-cmd.c (original)
+++ subversion/trunk/subversion/svn/mergeinfo-cmd.c Fri Dec 16 21:05:07 2011
@@ -114,6 +114,11 @@ svn_cl__mergeinfo(apr_getopt_t *os,
         tgt_peg_revision.kind = svn_opt_revision_base;
     }
 
+  SVN_ERR_W(svn_cl__check_related_source_and_target(source, &src_peg_revision,
+                                                    target, &tgt_peg_revision,
+                                                    ctx, pool),
+            _("Source and target must be different but related branches"));
+
   /* Do the real work, depending on the requested data flavor. */
   if (opt_state->show_revs == svn_cl__show_revs_merged)
     {

Modified: subversion/trunk/subversion/svn/util.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/util.c?rev=1215273&r1=1215272&r2=1215273&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/util.c (original)
+++ subversion/trunk/subversion/svn/util.c Fri Dec 16 21:05:07 2011
@@ -1423,3 +1423,43 @@ svn_cl__local_style_skip_ancestor(const 
 
   return svn_dirent_local_style(relpath ? relpath : path, pool);
 }
+
+/* Return a string of the form "PATH_OR_URL@REVISION". */
+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_ERR(svn_client__youngest_common_ancestor(
+            &ancestor_url, &ancestor_rev,
+            path_or_url1, revision1, path_or_url2, revision2,
+            ctx, pool, pool));
+
+  if (ancestor_url == NULL)
+    {
+      return 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));
+    }
+  return SVN_NO_ERROR;
+}



Re: svn commit: r1215273 - in /subversion/trunk/subversion: include/private/svn_client_private.h libsvn_client/client.h libsvn_client/ra.c svn/cl.h svn/merge-cmd.c svn/mergeinfo-cmd.c svn/util.c

Posted by Julian Foad <ju...@btopenworld.com>.
Hyrum K Wright wrote:

> julianfoad wrote:
>>  Reject attempts to merge between unrelated branches, [...].
> 
> We should probably make sure this change ends up in the 1.8 docs, as
> it potentially removes merging scenarios which folks may be using.

Noted in CHANGES.

- Julian

Re: svn commit: r1215273 - in /subversion/trunk/subversion: include/private/svn_client_private.h libsvn_client/client.h libsvn_client/ra.c svn/cl.h svn/merge-cmd.c svn/mergeinfo-cmd.c svn/util.c

Posted by Hyrum K Wright <hy...@wandisco.com>.
On Fri, Dec 16, 2011 at 3:05 PM,  <ju...@apache.org> wrote:
> Author: julianfoad
> Date: Fri Dec 16 21:05:07 2011
> New Revision: 1215273
>
> URL: http://svn.apache.org/viewvc?rev=1215273&view=rev
> Log:
> Reject attempts to merge between unrelated branches, or where the source and
> target are different objects within their respective branches.  These checks
> are applied in 'svn merge', only on 'reintegrate' and simple 'sync' merges
> where no revision range is specified.  They are also applied in the
> 'svn mergeinfo' command.

We should probably make sure this change ends up in the 1.8 docs, as
it potentially removes merging scenarios which folks may be using.

-Hyrum

>
> * subversion/include/private/svn_client_private.h,
>  subversion/libsvn_client/ra.c
>  (svn_client__youngest_common_ancestor): New function.
>
> * subversion/libsvn_client/client.h
>  (svn_client__get_youngest_common_ancestor): Cross-reference to
>    svn_client__youngest_common_ancestor().
>
> * subversion/svn/cl.h,
>  subversion/svn/util.c
>  (svn_cl__check_related_source_and_target, path_for_display): New functions.
>
> * 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.
>
> Modified:
>    subversion/trunk/subversion/include/private/svn_client_private.h
>    subversion/trunk/subversion/libsvn_client/client.h
>    subversion/trunk/subversion/libsvn_client/ra.c
>    subversion/trunk/subversion/svn/cl.h
>    subversion/trunk/subversion/svn/merge-cmd.c
>    subversion/trunk/subversion/svn/mergeinfo-cmd.c
>    subversion/trunk/subversion/svn/util.c
>
> Modified: subversion/trunk/subversion/include/private/svn_client_private.h
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_client_private.h?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/include/private/svn_client_private.h (original)
> +++ subversion/trunk/subversion/include/private/svn_client_private.h Fri Dec 16 21:05:07 2011
> @@ -59,6 +59,29 @@ svn_client__create_status(svn_client_sta
>                           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.  Set *ANCESTOR_RELPATH to
> + * NULL and *ANCESTOR_REVISION to SVN_INVALID_REVNUM if they have no
> + * common ancestor.  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.
> + *
> + * See also svn_client__get_youngest_common_ancestor().
> + */
> +svn_error_t *
> +svn_client__youngest_common_ancestor(const char **ancestor_url,
> +                                     svn_revnum_t *ancestor_rev,
> +                                     const char *path_or_url1,
> +                                     const svn_opt_revision_t *revision1,
> +                                     const char *path_or_url2,
> +                                     const svn_opt_revision_t *revision2,
> +                                     svn_client_ctx_t *ctx,
> +                                     apr_pool_t *result_pool,
> +                                     apr_pool_t *scratch_pool);
> +
>  #ifdef __cplusplus
>  }
>  #endif /* __cplusplus */
>
> Modified: subversion/trunk/subversion/libsvn_client/client.h
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/client.h?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/libsvn_client/client.h (original)
> +++ subversion/trunk/subversion/libsvn_client/client.h Fri Dec 16 21:05:07 2011
> @@ -199,6 +199,8 @@ svn_client__repos_location_segments(apr_
>
>    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,
>
> Modified: subversion/trunk/subversion/libsvn_client/ra.c
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/ra.c?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/libsvn_client/ra.c (original)
> +++ subversion/trunk/subversion/libsvn_client/ra.c Fri Dec 16 21:05:07 2011
> @@ -897,3 +897,36 @@ 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_rev,
> +                                     const char *path_or_url1,
> +                                     const svn_opt_revision_t *revision1,
> +                                     const char *path_or_url2,
> +                                     const svn_opt_revision_t *revision2,
> +                                     svn_client_ctx_t *ctx,
> +                                     apr_pool_t *result_pool,
> +                                     apr_pool_t *scratch_pool)
> +{
> +  apr_pool_t *sesspool = svn_pool_create(scratch_pool);
> +  svn_ra_session_t *session;
> +  const char *url1, *url2;
> +  svn_revnum_t rev1, rev2;
> +
> +  /* Resolve the two locations */
> +  SVN_ERR(svn_client__ra_session_from_path(&session, &rev1, &url1,
> +                                           path_or_url1, NULL,
> +                                           revision1, revision1,
> +                                           ctx, sesspool));
> +  SVN_ERR(resolve_rev_and_url(&rev2, &url2, session,
> +                              path_or_url2, revision2, revision2,
> +                              ctx, scratch_pool));
> +
> +  SVN_ERR(svn_client__get_youngest_common_ancestor(
> +            NULL, ancestor_url, ancestor_rev,
> +            url1, rev1, url2, rev2, ctx, result_pool));
> +
> +  svn_pool_destroy(sesspool);
> +  return SVN_NO_ERROR;
> +}
>
> Modified: subversion/trunk/subversion/svn/cl.h
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/svn/cl.h (original)
> +++ subversion/trunk/subversion/svn/cl.h Fri Dec 16 21:05:07 2011
> @@ -822,6 +822,23 @@ svn_cl__local_style_skip_ancestor(const
>                                   const char *path,
>                                   apr_pool_t *pool);
>
> +/* Check that PATH_OR_URL1@REVISION1 is related to PATH_OR_URL2@REVISION2.
> + * Raise an error if not.
> + *
> + * ### Ideally we would also check that they are on different lines of
> + * history.  That is easy in common cases, but to give a correct answer in
> + * general we need to know the operative revision(s) as well.  For example,
> + * when one location is the branch point from which the other branch was
> + * copied.
> + */
> +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 */
>
> Modified: subversion/trunk/subversion/svn/merge-cmd.c
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/merge-cmd.c?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/svn/merge-cmd.c (original)
> +++ subversion/trunk/subversion/svn/merge-cmd.c Fri Dec 16 21:05:07 2011
> @@ -112,6 +112,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. */
> @@ -347,6 +348,11 @@ svn_cl__merge(apr_getopt_t *os,
>
>   if (opt_state->reintegrate)
>     {
> +      SVN_ERR_W(svn_cl__check_related_source_and_target(
> +                  sourcepath1, &peg_revision1, targetpath, &unspecified,
> +                  ctx, pool),
> +                _("Source and target must be different but related branches"));
> +
>       err = merge_reintegrate(sourcepath1, &peg_revision1, targetpath,
>                               opt_state->dry_run, options, ctx, pool);
>     }
> @@ -364,6 +370,12 @@ 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;
> +
> +          /* This must be a 'sync' merge so check branch relationship. */
> +          SVN_ERR_W(svn_cl__check_related_source_and_target(
> +                      sourcepath1, &peg_revision1, targetpath, &unspecified,
> +                      ctx, pool),
> +                _("Source and target must be different but related branches"));
>         }
>
>       err = svn_client_merge_peg4(sourcepath1,
>
> Modified: subversion/trunk/subversion/svn/mergeinfo-cmd.c
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/mergeinfo-cmd.c?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/svn/mergeinfo-cmd.c (original)
> +++ subversion/trunk/subversion/svn/mergeinfo-cmd.c Fri Dec 16 21:05:07 2011
> @@ -114,6 +114,11 @@ svn_cl__mergeinfo(apr_getopt_t *os,
>         tgt_peg_revision.kind = svn_opt_revision_base;
>     }
>
> +  SVN_ERR_W(svn_cl__check_related_source_and_target(source, &src_peg_revision,
> +                                                    target, &tgt_peg_revision,
> +                                                    ctx, pool),
> +            _("Source and target must be different but related branches"));
> +
>   /* Do the real work, depending on the requested data flavor. */
>   if (opt_state->show_revs == svn_cl__show_revs_merged)
>     {
>
> Modified: subversion/trunk/subversion/svn/util.c
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/util.c?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/svn/util.c (original)
> +++ subversion/trunk/subversion/svn/util.c Fri Dec 16 21:05:07 2011
> @@ -1423,3 +1423,43 @@ svn_cl__local_style_skip_ancestor(const
>
>   return svn_dirent_local_style(relpath ? relpath : path, pool);
>  }
> +
> +/* Return a string of the form "PATH_OR_URL@REVISION". */
> +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_ERR(svn_client__youngest_common_ancestor(
> +            &ancestor_url, &ancestor_rev,
> +            path_or_url1, revision1, path_or_url2, revision2,
> +            ctx, pool, pool));
> +
> +  if (ancestor_url == NULL)
> +    {
> +      return 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));
> +    }
> +  return SVN_NO_ERROR;
> +}
>
>



-- 

uberSVN: Apache Subversion Made Easy
http://www.uberSVN.com/

Re: svn commit: r1215273 - in /subversion/trunk/subversion: include/private/svn_client_private.h libsvn_client/client.h libsvn_client/ra.c svn/cl.h svn/merge-cmd.c svn/mergeinfo-cmd.c svn/util.c

Posted by Hyrum K Wright <hy...@wandisco.com>.
On Fri, Dec 16, 2011 at 3:05 PM,  <ju...@apache.org> wrote:
> Author: julianfoad
> Date: Fri Dec 16 21:05:07 2011
> New Revision: 1215273
>
> URL: http://svn.apache.org/viewvc?rev=1215273&view=rev
> Log:
> Reject attempts to merge between unrelated branches, or where the source and
> target are different objects within their respective branches.  These checks
> are applied in 'svn merge', only on 'reintegrate' and simple 'sync' merges
> where no revision range is specified.  They are also applied in the
> 'svn mergeinfo' command.

We should probably make sure this change ends up in the 1.8 docs, as
it potentially removes merging scenarios which folks may be using.

-Hyrum

>
> * subversion/include/private/svn_client_private.h,
>  subversion/libsvn_client/ra.c
>  (svn_client__youngest_common_ancestor): New function.
>
> * subversion/libsvn_client/client.h
>  (svn_client__get_youngest_common_ancestor): Cross-reference to
>    svn_client__youngest_common_ancestor().
>
> * subversion/svn/cl.h,
>  subversion/svn/util.c
>  (svn_cl__check_related_source_and_target, path_for_display): New functions.
>
> * 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.
>
> Modified:
>    subversion/trunk/subversion/include/private/svn_client_private.h
>    subversion/trunk/subversion/libsvn_client/client.h
>    subversion/trunk/subversion/libsvn_client/ra.c
>    subversion/trunk/subversion/svn/cl.h
>    subversion/trunk/subversion/svn/merge-cmd.c
>    subversion/trunk/subversion/svn/mergeinfo-cmd.c
>    subversion/trunk/subversion/svn/util.c
>
> Modified: subversion/trunk/subversion/include/private/svn_client_private.h
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_client_private.h?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/include/private/svn_client_private.h (original)
> +++ subversion/trunk/subversion/include/private/svn_client_private.h Fri Dec 16 21:05:07 2011
> @@ -59,6 +59,29 @@ svn_client__create_status(svn_client_sta
>                           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.  Set *ANCESTOR_RELPATH to
> + * NULL and *ANCESTOR_REVISION to SVN_INVALID_REVNUM if they have no
> + * common ancestor.  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.
> + *
> + * See also svn_client__get_youngest_common_ancestor().
> + */
> +svn_error_t *
> +svn_client__youngest_common_ancestor(const char **ancestor_url,
> +                                     svn_revnum_t *ancestor_rev,
> +                                     const char *path_or_url1,
> +                                     const svn_opt_revision_t *revision1,
> +                                     const char *path_or_url2,
> +                                     const svn_opt_revision_t *revision2,
> +                                     svn_client_ctx_t *ctx,
> +                                     apr_pool_t *result_pool,
> +                                     apr_pool_t *scratch_pool);
> +
>  #ifdef __cplusplus
>  }
>  #endif /* __cplusplus */
>
> Modified: subversion/trunk/subversion/libsvn_client/client.h
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/client.h?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/libsvn_client/client.h (original)
> +++ subversion/trunk/subversion/libsvn_client/client.h Fri Dec 16 21:05:07 2011
> @@ -199,6 +199,8 @@ svn_client__repos_location_segments(apr_
>
>    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,
>
> Modified: subversion/trunk/subversion/libsvn_client/ra.c
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/ra.c?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/libsvn_client/ra.c (original)
> +++ subversion/trunk/subversion/libsvn_client/ra.c Fri Dec 16 21:05:07 2011
> @@ -897,3 +897,36 @@ 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_rev,
> +                                     const char *path_or_url1,
> +                                     const svn_opt_revision_t *revision1,
> +                                     const char *path_or_url2,
> +                                     const svn_opt_revision_t *revision2,
> +                                     svn_client_ctx_t *ctx,
> +                                     apr_pool_t *result_pool,
> +                                     apr_pool_t *scratch_pool)
> +{
> +  apr_pool_t *sesspool = svn_pool_create(scratch_pool);
> +  svn_ra_session_t *session;
> +  const char *url1, *url2;
> +  svn_revnum_t rev1, rev2;
> +
> +  /* Resolve the two locations */
> +  SVN_ERR(svn_client__ra_session_from_path(&session, &rev1, &url1,
> +                                           path_or_url1, NULL,
> +                                           revision1, revision1,
> +                                           ctx, sesspool));
> +  SVN_ERR(resolve_rev_and_url(&rev2, &url2, session,
> +                              path_or_url2, revision2, revision2,
> +                              ctx, scratch_pool));
> +
> +  SVN_ERR(svn_client__get_youngest_common_ancestor(
> +            NULL, ancestor_url, ancestor_rev,
> +            url1, rev1, url2, rev2, ctx, result_pool));
> +
> +  svn_pool_destroy(sesspool);
> +  return SVN_NO_ERROR;
> +}
>
> Modified: subversion/trunk/subversion/svn/cl.h
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/svn/cl.h (original)
> +++ subversion/trunk/subversion/svn/cl.h Fri Dec 16 21:05:07 2011
> @@ -822,6 +822,23 @@ svn_cl__local_style_skip_ancestor(const
>                                   const char *path,
>                                   apr_pool_t *pool);
>
> +/* Check that PATH_OR_URL1@REVISION1 is related to PATH_OR_URL2@REVISION2.
> + * Raise an error if not.
> + *
> + * ### Ideally we would also check that they are on different lines of
> + * history.  That is easy in common cases, but to give a correct answer in
> + * general we need to know the operative revision(s) as well.  For example,
> + * when one location is the branch point from which the other branch was
> + * copied.
> + */
> +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 */
>
> Modified: subversion/trunk/subversion/svn/merge-cmd.c
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/merge-cmd.c?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/svn/merge-cmd.c (original)
> +++ subversion/trunk/subversion/svn/merge-cmd.c Fri Dec 16 21:05:07 2011
> @@ -112,6 +112,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. */
> @@ -347,6 +348,11 @@ svn_cl__merge(apr_getopt_t *os,
>
>   if (opt_state->reintegrate)
>     {
> +      SVN_ERR_W(svn_cl__check_related_source_and_target(
> +                  sourcepath1, &peg_revision1, targetpath, &unspecified,
> +                  ctx, pool),
> +                _("Source and target must be different but related branches"));
> +
>       err = merge_reintegrate(sourcepath1, &peg_revision1, targetpath,
>                               opt_state->dry_run, options, ctx, pool);
>     }
> @@ -364,6 +370,12 @@ 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;
> +
> +          /* This must be a 'sync' merge so check branch relationship. */
> +          SVN_ERR_W(svn_cl__check_related_source_and_target(
> +                      sourcepath1, &peg_revision1, targetpath, &unspecified,
> +                      ctx, pool),
> +                _("Source and target must be different but related branches"));
>         }
>
>       err = svn_client_merge_peg4(sourcepath1,
>
> Modified: subversion/trunk/subversion/svn/mergeinfo-cmd.c
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/mergeinfo-cmd.c?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/svn/mergeinfo-cmd.c (original)
> +++ subversion/trunk/subversion/svn/mergeinfo-cmd.c Fri Dec 16 21:05:07 2011
> @@ -114,6 +114,11 @@ svn_cl__mergeinfo(apr_getopt_t *os,
>         tgt_peg_revision.kind = svn_opt_revision_base;
>     }
>
> +  SVN_ERR_W(svn_cl__check_related_source_and_target(source, &src_peg_revision,
> +                                                    target, &tgt_peg_revision,
> +                                                    ctx, pool),
> +            _("Source and target must be different but related branches"));
> +
>   /* Do the real work, depending on the requested data flavor. */
>   if (opt_state->show_revs == svn_cl__show_revs_merged)
>     {
>
> Modified: subversion/trunk/subversion/svn/util.c
> URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/util.c?rev=1215273&r1=1215272&r2=1215273&view=diff
> ==============================================================================
> --- subversion/trunk/subversion/svn/util.c (original)
> +++ subversion/trunk/subversion/svn/util.c Fri Dec 16 21:05:07 2011
> @@ -1423,3 +1423,43 @@ svn_cl__local_style_skip_ancestor(const
>
>   return svn_dirent_local_style(relpath ? relpath : path, pool);
>  }
> +
> +/* Return a string of the form "PATH_OR_URL@REVISION". */
> +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_ERR(svn_client__youngest_common_ancestor(
> +            &ancestor_url, &ancestor_rev,
> +            path_or_url1, revision1, path_or_url2, revision2,
> +            ctx, pool, pool));
> +
> +  if (ancestor_url == NULL)
> +    {
> +      return 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));
> +    }
> +  return SVN_NO_ERROR;
> +}
>
>



-- 

uberSVN: Apache Subversion Made Easy
http://www.uberSVN.com/