On Fri, Dec 16, 2011 at 3:05 PM, <julianf...@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/