Author: stsp Date: Wed Oct 27 14:16:37 2010 New Revision: 1027970 URL: http://svn.apache.org/viewvc?rev=1027970&view=rev Log: Disallow merges into mixed-revision working copies by default.
Merging into mixed-rev WCs has always been considered bad practice. The Subversion Book is discouraging it. 'svn merge' will now ask the user to run 'svn update' if the merge target is a mixed-rev WC. A new option --allow-mixed-revisions has been added for those who really need to merge into a mixed-rev WC for some reason. Using it is not recommended. It was used to fix failing regression tests which merge into mixed-rev WCs. This helps us maintain the current degree of test coverage of merges into mixed-rev WCs. Reintegrate merges into mixed-revision working copies have always been disallowed, so the new option cannot be used with --reintegrate. All types of merges are affected by this change, including --dry-run and --no-ancestry merges. See for related discussion: Date: Fri, 1 Oct 2010 00:40:35 +0200 From: Stefan Sperling To: dev@ Subject: disallow merges into mixed-rev WCs by default? Message-ID: <[email protected]> http://svn.haxx.se/dev/archive-2010-10/0000.shtml * subversion/svn/merge-cmd.c (svn_cl__merge): Reject --allow-mixed-revisions in combintation with --reintegrate. Call svn_client_merge_peg4() and svn_client_merge4(). * subversion/svn/cl.h (svn_cl__opt_state_t): Add 'allow_mixed_rev'. * subversion/svn/main.c (svn_cl__longopt_t): Add 'opt_allow_mixed_revisions'. (svn_cl__options): Define new --allow-mixed-revisions option and add it to 'svn merge'. (main): Handle new --allow-mixed-revisions option. * subversion/include/svn_client.h (svn_client_merge4, svn_client_merge_peg4): Declare. (svn_client_merge3, svn_client_merge_peg3): Deprecate. * subversion/libsvn_client/deprecated.c (svn_client_merge3, svn_client_merge_peg3): Re-implement as wrappers around newer revisions of these functions. * subversion/libsvn_client/merge.c (ensure_wc_is_suitable_merge_target): New, replacing and based on ... (ensure_wc_reflects_repository_subtree): ... this function. (merge_locked, merge_peg_locked): Add 'allow_mixed_rev' parameter. Check the merge target with the ensure_wc_is_suitable_merge_target() helper function before carrying out the merge. (merge_baton, merge_cb, merge_peg_baton, merge_peg_cb): Add 'allow_mixed_rev'. (svn_client_merge4, svn_client_merge_peg4): Add 'allow_mixed_rev' parameter and pass it into the callback baton. * subversion/tests/cmdline/mergeinfo_tests.py (mergeinfo_on_pegged_wc_path): Allow merge into mixed-rev WC. * subversion/tests/cmdline/special_tests.py (merge_file_into_symlink): Allow merge into mixed-rev WC. * subversion/tests/cmdline/merge_tree_conflict_tests.py (three_way_merge_add_of_existing_binary_file): Allow merge into mixed-rev WC. * subversion/tests/cmdline/merge_tests.py (svn_merge): Accept a varargs parameter ARGS which is used to pass additional options to 'svn merge'. (textual_merges_galore, merge_binary_file, merge_skips_obstructions, merge_into_missing, merge_dir_branches, merge_file_replace_to_mixed_rev_wc, merge_ignore_whitespace, merge_ignore_eolstyle, merge_to_path_with_switched_children, new_subtrees_should_not_break_merge, dont_add_mergeinfo_from_own_history, reverse_merge_away_all_mergeinfo, merge_two_edits_to_same_prop, merge_an_eol_unification_and_set_svn_eol_style, merge_automatic_conflict_resolution, committed_case_only_move_and_revert, foreign_repos_del_and_props): Allow merge into mixed-rev WC. * subversion/tests/cmdline/tree_conflict_tests.py (ensure_tree_conflict): Allow merge into mixed-rev WC. * subversion/tests/cmdline/log_tests.py (merge_history_repos): Allow merge into mixed-rev WC. * subversion/tests/cmdline/resolve_tests.py (automatic_conflict_resolution): Allow merge into mixed-rev WC. * subversion/tests/cmdline/merge_reintegrate_tests.py (reintegrate_fail_on_modified_wc, reintegrate_fail_on_mixed_rev_wc, reintegrate_fail_on_switched_wc): Adjust expected stderr. (reintegrate_with_subtree_mergeinfo, multiple_reintegrates_from_the_same_branch): Allow merges into mixed-rev WC. Modified: subversion/trunk/subversion/include/svn_client.h subversion/trunk/subversion/libsvn_client/deprecated.c subversion/trunk/subversion/libsvn_client/merge.c subversion/trunk/subversion/svn/cl.h subversion/trunk/subversion/svn/main.c subversion/trunk/subversion/svn/merge-cmd.c subversion/trunk/subversion/tests/cmdline/log_tests.py subversion/trunk/subversion/tests/cmdline/merge_reintegrate_tests.py subversion/trunk/subversion/tests/cmdline/merge_tests.py subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py subversion/trunk/subversion/tests/cmdline/mergeinfo_tests.py subversion/trunk/subversion/tests/cmdline/resolve_tests.py subversion/trunk/subversion/tests/cmdline/special_tests.py subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py Modified: subversion/trunk/subversion/include/svn_client.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/include/svn_client.h (original) +++ subversion/trunk/subversion/include/svn_client.h Wed Oct 27 14:16:37 2010 @@ -3068,9 +3068,39 @@ svn_client_diff_summarize_peg(const char * If @a dry_run is TRUE, the merge is carried out, and full notification * feedback is provided, but the working copy is not modified. * + * If allow_mixed_rev is @c FALSE, and @a merge_target is a mixed-revision + * working copy, raise @c SVN_ERR_CLIENT_NOT_READY_TO_MERGE. + * Because users rarely intend to merge into mixed-revision working copies, + * it is recommended to set this parameter to FALSE by default unless the + * user has explicitly requested a merge into a mixed-revision working copy. + * * The authentication baton cached in @a ctx is used to communicate with the * repository. * + * @since New in 1.7. + */ +svn_error_t * +svn_client_merge4(const char *source1, + const svn_opt_revision_t *revision1, + const char *source2, + const svn_opt_revision_t *revision2, + const char *target_wcpath, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_boolean_t force, + svn_boolean_t record_only, + svn_boolean_t dry_run, + svn_boolean_t allow_mixed_rev, + const apr_array_header_t *merge_options, + svn_client_ctx_t *ctx, + apr_pool_t *pool); + +/** + * Similar to svn_client_merge4(), but with @a allow_mixed_rev set to + * @c TRUE. + * + * @deprecated Provided for backward compatibility with the 1.6 API. + * * @since New in 1.5. */ svn_error_t * @@ -3172,7 +3202,30 @@ svn_client_merge_reintegrate(const char * list of provided ranges has an `unspecified' or unrecognized * `kind', return #SVN_ERR_CLIENT_BAD_REVISION. * - * All other options are handled identically to svn_client_merge3(). + * All other options are handled identically to svn_client_merge4(). + * + * @since New in 1.7. + */ +svn_error_t * +svn_client_merge_peg4(const char *source, + const apr_array_header_t *ranges_to_merge, + const svn_opt_revision_t *peg_revision, + const char *target_wcpath, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_boolean_t force, + svn_boolean_t record_only, + svn_boolean_t dry_run, + svn_boolean_t allow_mixed_rev, + const apr_array_header_t *merge_options, + svn_client_ctx_t *ctx, + apr_pool_t *pool); + +/** + * Similar to svn_client_merge_peg4(), but with @a allow_mixed_rev set to + * @c TRUE. + * + * @deprecated Provided for backward compatibility with the 1.6 API. * * @since New in 1.5. */ Modified: subversion/trunk/subversion/libsvn_client/deprecated.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/deprecated.c?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_client/deprecated.c (original) +++ subversion/trunk/subversion/libsvn_client/deprecated.c Wed Oct 27 14:16:37 2010 @@ -1338,6 +1338,27 @@ svn_client_log(const apr_array_header_t /*** From merge.c ***/ svn_error_t * +svn_client_merge3(const char *source1, + const svn_opt_revision_t *revision1, + const char *source2, + const svn_opt_revision_t *revision2, + const char *target_wcpath, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_boolean_t force, + svn_boolean_t record_only, + svn_boolean_t dry_run, + const apr_array_header_t *merge_options, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + return svn_client_merge4(source1, revision1, source2, revision2, + target_wcpath, depth, ignore_ancestry, force, + record_only, dry_run, TRUE, merge_options, + ctx, pool); +} + +svn_error_t * svn_client_merge2(const char *source1, const svn_opt_revision_t *revision1, const char *source2, @@ -1376,7 +1397,25 @@ svn_client_merge(const char *source1, dry_run, NULL, ctx, pool); } - +svn_error_t * +svn_client_merge_peg3(const char *source, + const apr_array_header_t *ranges_to_merge, + const svn_opt_revision_t *peg_revision, + const char *target_wcpath, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_boolean_t force, + svn_boolean_t record_only, + svn_boolean_t dry_run, + const apr_array_header_t *merge_options, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + return svn_client_merge_peg4(source, ranges_to_merge, peg_revision, + target_wcpath, depth, ignore_ancestry, force, + record_only, dry_run, TRUE, merge_options, + ctx, pool); +} svn_error_t * svn_client_merge_peg2(const char *source, Modified: subversion/trunk/subversion/libsvn_client/merge.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_client/merge.c (original) +++ subversion/trunk/subversion/libsvn_client/merge.c Wed Oct 27 14:16:37 2010 @@ -8862,6 +8862,73 @@ merge_cousins_and_supplement_mergeinfo(c return SVN_NO_ERROR; } +/* Perform checks to determine whether of the working copy at TARGET_ABSPATH + * can safely be used as a merge target. Checks are performed according to + * the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES + * parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE. + * + * E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must + * be a single-revision, pristine, unswitched working copy. + * In other words, it must reflect a subtree of the repostiory as found + * at single revision -- although sparse checkouts are permitted. */ +static svn_error_t * +ensure_wc_is_suitable_merge_target(const char *target_abspath, + svn_client_ctx_t *ctx, + svn_boolean_t allow_mixed_rev, + svn_boolean_t allow_local_mods, + svn_boolean_t allow_switched_subtrees, + apr_pool_t *scratch_pool) +{ + svn_wc_revision_status_t *wc_stat; + + /* Avoid the following status crawl if we don't need it. */ + if (allow_mixed_rev && allow_local_mods && allow_switched_subtrees) + return SVN_NO_ERROR; + + /* Get a WC summary with min/max revisions set to the BASE revision. */ + SVN_ERR(svn_wc_revision_status2(&wc_stat, ctx->wc_ctx, target_abspath, NULL, + FALSE, ctx->cancel_func, ctx->cancel_baton, + scratch_pool, scratch_pool)); + + if (! allow_switched_subtrees && wc_stat->switched) + return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, + _("Cannot merge into a working copy " + "with a switched subtree")); + + if (! allow_local_mods && wc_stat->modified) + return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, + _("Cannot merge into a working copy " + "that has local modifications")); + + if (! allow_mixed_rev) + { + if (! (SVN_IS_VALID_REVNUM(wc_stat->min_rev) + && SVN_IS_VALID_REVNUM(wc_stat->max_rev))) + { + svn_boolean_t is_added; + + /* Allow merge into added nodes. */ + SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath, + scratch_pool)); + if (is_added) + return SVN_NO_ERROR; + else + return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, + _("Cannot determine revision of working " + "copy")); + } + + if (wc_stat->min_rev != wc_stat->max_rev) + return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, + _("Cannot merge into mixed-revision working " + "copy [%lu:%lu]; try updating first"), + wc_stat->min_rev, wc_stat->max_rev); + } + + return SVN_NO_ERROR; +} + + /*-----------------------------------------------------------------------*/ /*** Public APIs ***/ @@ -8877,6 +8944,7 @@ merge_locked(const char *source1, svn_boolean_t force, svn_boolean_t record_only, svn_boolean_t dry_run, + svn_boolean_t allow_mixed_rev, const apr_array_header_t *merge_options, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) @@ -8952,6 +9020,12 @@ merge_locked(const char *source1, _("Merge target '%s' does not exist in the " "working copy"), target_abspath)); + + /* Do not allow merges into mixed-revision working copies. */ + SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx, + allow_mixed_rev, TRUE, TRUE, + scratch_pool)); + /* Determine the working copy target's repository root URL. */ working_rev.kind = svn_opt_revision_working; SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_abspath, @@ -9150,6 +9224,7 @@ struct merge_baton { svn_boolean_t force; svn_boolean_t record_only; svn_boolean_t dry_run; + svn_boolean_t allow_mixed_rev; const apr_array_header_t *merge_options; svn_client_ctx_t *ctx; }; @@ -9163,7 +9238,8 @@ merge_cb(void *baton, apr_pool_t *result SVN_ERR(merge_locked(b->source1, b->revision1, b->source2, b->revision2, b->target_abspath, b->depth, b->ignore_ancestry, b->force, b->record_only, b->dry_run, - b->merge_options, b->ctx, scratch_pool)); + b->allow_mixed_rev, b->merge_options, b->ctx, + scratch_pool)); return SVN_NO_ERROR; } @@ -9191,7 +9267,7 @@ get_target_and_lock_abspath(const char * } svn_error_t * -svn_client_merge3(const char *source1, +svn_client_merge4(const char *source1, const svn_opt_revision_t *revision1, const char *source2, const svn_opt_revision_t *revision2, @@ -9201,6 +9277,7 @@ svn_client_merge3(const char *source1, svn_boolean_t force, svn_boolean_t record_only, svn_boolean_t dry_run, + svn_boolean_t allow_mixed_rev, const apr_array_header_t *merge_options, svn_client_ctx_t *ctx, apr_pool_t *pool) @@ -9221,6 +9298,7 @@ svn_client_merge3(const char *source1, baton.force = force; baton.record_only = record_only; baton.dry_run = dry_run; + baton.allow_mixed_rev = allow_mixed_rev; baton.merge_options = merge_options; baton.ctx = ctx; @@ -9235,45 +9313,6 @@ svn_client_merge3(const char *source1, } -/* If TARGET_WCPATH does not reflect a single-revision, pristine, - unswitched working copy -- in other words, a subtree found in a - single revision (although sparse checkouts are permitted) -- raise - SVN_ERR_CLIENT_NOT_READY_TO_MERGE. */ -static svn_error_t * -ensure_wc_reflects_repository_subtree(const char *target_abspath, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) -{ - svn_wc_revision_status_t *wc_stat; - - /* Get a WC summary with min/max revisions set to the BASE revision. */ - SVN_ERR(svn_wc_revision_status2(&wc_stat, ctx->wc_ctx, target_abspath, NULL, - FALSE, ctx->cancel_func, ctx->cancel_baton, - scratch_pool, scratch_pool)); - - if (wc_stat->switched) - return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, - _("Cannot reintegrate into a working copy " - "with a switched subtree")); - - if (wc_stat->modified) - return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, - _("Cannot reintegrate into a working copy " - "that has local modifications")); - - if (! (SVN_IS_VALID_REVNUM(wc_stat->min_rev) - && SVN_IS_VALID_REVNUM(wc_stat->max_rev))) - return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, - _("Cannot determine revision of working copy")); - - if (wc_stat->min_rev != wc_stat->max_rev) - return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, - _("Cannot reintegrate into mixed-revision " - "working copy; try updating first")); - - return SVN_NO_ERROR; -} - /* Check if mergeinfo for a given path is described explicitly or via inheritance in a mergeinfo catalog. @@ -10221,8 +10260,11 @@ merge_reintegrate_locked(const char *sou svn_dirent_local_style(target_abspath, scratch_pool)); - SVN_ERR(ensure_wc_reflects_repository_subtree(target_abspath, ctx, - scratch_pool)); + /* A reintegrate merge requires the merge target to reflect a subtree + * of the repository as found at a single revision. */ + SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx, + FALSE, FALSE, FALSE, + scratch_pool)); SVN_ERR(svn_wc__node_get_base_rev(&target_base_rev, ctx->wc_ctx, target_abspath, scratch_pool)); @@ -10438,6 +10480,7 @@ merge_peg_locked(const char *source, svn_boolean_t force, svn_boolean_t record_only, svn_boolean_t dry_run, + svn_boolean_t allow_mixed_rev, const apr_array_header_t *merge_options, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) @@ -10479,6 +10522,10 @@ merge_peg_locked(const char *source, _("Merge target '%s' does not exist in the " "working copy"), target_abspath)); + SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx, + allow_mixed_rev, TRUE, TRUE, + scratch_pool)); + /* Determine the working copy target's repository root URL. */ working_rev.kind = svn_opt_revision_working; SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_abspath, @@ -10541,6 +10588,7 @@ struct merge_peg_baton { svn_boolean_t force; svn_boolean_t record_only; svn_boolean_t dry_run; + svn_boolean_t allow_mixed_rev; const apr_array_header_t *merge_options; svn_client_ctx_t *ctx; }; @@ -10554,13 +10602,14 @@ merge_peg_cb(void *baton, apr_pool_t *re SVN_ERR(merge_peg_locked(b->source, b->ranges_to_merge, b->peg_revision, b->target_abspath, b->depth, b->ignore_ancestry, b->force, b->record_only, b->dry_run, - b->merge_options, b->ctx, scratch_pool)); + b->allow_mixed_rev, b->merge_options, b->ctx, + scratch_pool)); return SVN_NO_ERROR; } svn_error_t * -svn_client_merge_peg3(const char *source, +svn_client_merge_peg4(const char *source, const apr_array_header_t *ranges_to_merge, const svn_opt_revision_t *peg_revision, const char *target_wcpath, @@ -10569,6 +10618,7 @@ svn_client_merge_peg3(const char *source svn_boolean_t force, svn_boolean_t record_only, svn_boolean_t dry_run, + svn_boolean_t allow_mixed_rev, const apr_array_header_t *merge_options, svn_client_ctx_t *ctx, apr_pool_t *pool) @@ -10592,6 +10642,7 @@ svn_client_merge_peg3(const char *source baton.force = force; baton.record_only = record_only; baton.dry_run = dry_run; + baton.allow_mixed_rev = allow_mixed_rev; baton.merge_options = merge_options; baton.ctx = ctx; Modified: subversion/trunk/subversion/svn/cl.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/svn/cl.h (original) +++ subversion/trunk/subversion/svn/cl.h Wed Oct 27 14:16:37 2010 @@ -229,6 +229,7 @@ typedef struct svn_cl__opt_state_t svn_boolean_t show_diff; /* produce diff output (maps to --diff) */ svn_boolean_t internal_diff; /* override diff_cmd in config file */ svn_boolean_t use_git_diff_format; /* Use git's extended diff format */ + svn_boolean_t allow_mixed_rev; /* Allow operation on mixed-revision WC */ } svn_cl__opt_state_t; Modified: subversion/trunk/subversion/svn/main.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/main.c?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/svn/main.c (original) +++ subversion/trunk/subversion/svn/main.c Wed Oct 27 14:16:37 2010 @@ -122,6 +122,7 @@ typedef enum { opt_diff, opt_internal_diff, opt_use_git_diff_format, + opt_allow_mixed_revisions, } svn_cl__longopt_t; #define SVN_CL__OPTION_CONTINUATION_INDENT " " @@ -332,6 +333,12 @@ const apr_getopt_option_t svn_cl__option N_("override diff-cmd specified in config file")}, {"git", opt_use_git_diff_format, 0, N_("use git's extended diff format")}, + {"allow-mixed-revisions", opt_allow_mixed_revisions, 0, + N_("Allow merge into mixed-revision working copy.\n" + SVN_CL__OPTION_CONTINUATION_INDENT + "Use of this option is not recommended!\n" + SVN_CL__OPTION_CONTINUATION_INDENT + "Please run 'svn update' instead")}, /* Long-opt Aliases * @@ -718,7 +725,8 @@ const svn_opt_subcommand_desc2_t svn_cl_ " The --ignore-ancestry option overrides this, forcing Subversion to\n" " regard the sources as unrelated and not to track the merge.\n"), {'r', 'c', 'N', opt_depth, 'q', opt_force, opt_dry_run, opt_merge_cmd, - opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate} }, + opt_record_only, 'x', opt_ignore_ancestry, opt_accept, opt_reintegrate, + opt_allow_mixed_revisions} }, { "mergeinfo", svn_cl__mergeinfo, {0}, N_ ("Display merge-related information.\n" @@ -1782,6 +1790,9 @@ main(int argc, const char *argv[]) case opt_use_git_diff_format: opt_state.use_git_diff_format = TRUE; break; + case opt_allow_mixed_revisions: + opt_state.allow_mixed_rev = TRUE; + break; default: /* Hmmm. Perhaps this would be a good place to squirrel away opts that commands like svn diff might need. Hmmm indeed. */ Modified: subversion/trunk/subversion/svn/merge-cmd.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/merge-cmd.c?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/svn/merge-cmd.c (original) +++ subversion/trunk/subversion/svn/merge-cmd.c Wed Oct 27 14:16:37 2010 @@ -293,6 +293,10 @@ svn_cl__merge(apr_getopt_t *os, return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("--reintegrate can only be used with " "a single merge source")); + if (opt_state->allow_mixed_rev) + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("--allow-mixed-revisions cannot be used " + "with --reintegrate")); } if (! two_sources_specified) /* TODO: Switch order of if */ @@ -322,7 +326,7 @@ svn_cl__merge(apr_getopt_t *os, opt_state->dry_run, options, ctx, pool); else - err = svn_client_merge_peg3(sourcepath1, + err = svn_client_merge_peg4(sourcepath1, ranges_to_merge, &peg_revision1, targetpath, @@ -331,6 +335,7 @@ svn_cl__merge(apr_getopt_t *os, opt_state->force, opt_state->record_only, opt_state->dry_run, + opt_state->allow_mixed_rev, options, ctx, pool); @@ -342,7 +347,7 @@ svn_cl__merge(apr_getopt_t *os, NULL, _("Merge sources must both be " "either paths or URLs"))); - err = svn_client_merge3(sourcepath1, + err = svn_client_merge4(sourcepath1, &first_range_start, sourcepath2, &first_range_end, @@ -352,6 +357,7 @@ svn_cl__merge(apr_getopt_t *os, opt_state->force, opt_state->record_only, opt_state->dry_run, + opt_state->allow_mixed_rev, options, ctx, pool); Modified: subversion/trunk/subversion/tests/cmdline/log_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/log_tests.py?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/log_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/log_tests.py Wed Oct 27 14:16:37 2010 @@ -298,7 +298,8 @@ def merge_history_repos(sbox): # Mergeinfo changes on /trunk: # Merged /branches/a:r7 os.chdir('trunk') - svntest.main.run_svn(None, 'merge', '--record-only', '-r6:7', + svntest.main.run_svn(None, 'merge', '--allow-mixed-revisions', + '--record-only', '-r6:7', os.path.join('..', branch_a)) svntest.main.run_svn(None, 'ci', '-m', "Block r7 from merging to trunk.", @@ -392,7 +393,8 @@ def merge_history_repos(sbox): # Merged /trunk:r2 # Merged /branches/c:r3-16 os.chdir('trunk') - svntest.main.run_svn(None, 'merge', os.path.join('..', branch_c) + '@HEAD') + svntest.main.run_svn(None, 'merge', '--allow-mixed-revisions', + os.path.join('..', branch_c) + '@HEAD') svntest.main.file_write(os.path.join('A', 'mu'), "This is the file 'mu'.\n" + "Don't forget to look at 'upsilon', as well.\n" + Modified: subversion/trunk/subversion/tests/cmdline/merge_reintegrate_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/merge_reintegrate_tests.py?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/merge_reintegrate_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/merge_reintegrate_tests.py Wed Oct 27 14:16:37 2010 @@ -669,7 +669,7 @@ def reintegrate_fail_on_modified_wc(sbox svntest.actions.run_and_verify_merge( A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, None, None, None, - ".*Cannot reintegrate into a working copy that has local modifications.*", + ".*Cannot merge into a working copy that has local modifications.*", None, None, None, None, True, False, '--reintegrate', A_path) #---------------------------------------------------------------------- @@ -692,7 +692,7 @@ def reintegrate_fail_on_mixed_rev_wc(sbo svntest.actions.run_and_verify_merge( A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, None, None, None, - ".*Cannot reintegrate into mixed-revision working copy.*", + ".*Cannot merge into mixed-revision working copy.*", None, None, None, None, True, False, '--reintegrate', A_path) #---------------------------------------------------------------------- @@ -737,7 +737,7 @@ def reintegrate_fail_on_switched_wc(sbox svntest.actions.run_and_verify_merge( A_path, None, None, sbox.repo_url + '/A_COPY', None, None, None, None, None, None, None, - ".*Cannot reintegrate into a working copy with a switched subtree.*", + ".*Cannot merge into a working copy with a switched subtree.*", None, None, None, None, True, False, '--reintegrate', A_path) #---------------------------------------------------------------------- @@ -1319,7 +1319,7 @@ def reintegrate_with_subtree_mergeinfo(s ' U ' + A_COPY_path + '\n', ' U ' + D_COPY_path + '\n', ' U ' + gamma_moved_COPY_path + '\n']), - [], 'merge', sbox.repo_url + '/A', A_COPY_path) + [], 'merge', '--allow-mixed-revisions', sbox.repo_url + '/A', A_COPY_path) expected_output = wc.State( wc_dir, {'A_COPY' : Item(verb='Sending'), # Mergeinfo update @@ -1611,7 +1611,7 @@ def multiple_reintegrates_from_the_same_ expected_merge_output([[2,3],[2,16]], ['U ' + psi_COPY_path + '\n', ' U ' + A_COPY_path + '\n',]), - [], 'merge', sbox.repo_url + '/A', A_COPY_path) + [], 'merge', '--allow-mixed-revisions', sbox.repo_url + '/A', A_COPY_path) #---------------------------------------------------------------------- # Test for a reintegrate bug which can occur when the merge source Modified: subversion/trunk/subversion/tests/cmdline/merge_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/merge_tests.py?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/merge_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/merge_tests.py Wed Oct 27 14:16:37 2010 @@ -305,7 +305,10 @@ def textual_merges_galore(sbox): expected_skip, None, svntest.tree.detect_conflict_files, - list(tau_conflict_support_files)) + (list(tau_conflict_support_files)), + None, None, False, True, + '--allow-mixed-revisions', + other_wc) # Now reverse merge r3 into A/D/G/rho, give it non-conflicting local # mods, then merge in the 2:3 change. ### Not bothering to do the @@ -1258,7 +1261,8 @@ def merge_binary_file(sbox): expected_status, expected_skip, None, None, None, None, None, - 1) + True, True, '--allow-mixed-revisions', + other_wc) #---------------------------------------------------------------------- # Regression test for Issue #1297: @@ -1531,7 +1535,8 @@ def merge_skips_obstructions(sbox): expected_status.copy(wc_dir), expected_skip, None, None, None, None, None, - 1, 0) + True, False, '--allow-mixed-revisions', + wc_dir) # Revert the local mods, and commit a change to A/B/lambda (r4), and then # commit the deletion of the same file. (r5) @@ -1595,7 +1600,8 @@ def merge_skips_obstructions(sbox): expected_status_short, expected_skip, None, None, None, None, None, - 1, 0) + True, False, '--allow-mixed-revisions', + wc_dir) # OK, so let's commit the new lambda (r6), and then delete the # working file. Then re-run the -r3:4 merge, and see how svn deals @@ -1641,7 +1647,8 @@ def merge_skips_obstructions(sbox): expected_status.copy(wc_dir), expected_skip, None, None, None, None, None, - 1, 0, '--ignore-ancestry', wc_dir) + 1, 0, '--ignore-ancestry', + '--allow-mixed-revisions', wc_dir) #---------------------------------------------------------------------- # At one time, a merge that added items with the same name as missing @@ -1750,7 +1757,9 @@ def merge_into_missing(sbox): expected_skip, None, None, None, None, None, 0, 0, '--dry-run', - '--ignore-ancestry', F_path) + '--ignore-ancestry', + '--allow-mixed-revisions', + F_path) expected_status = wc.State(F_path, { '' : Item(status=' ', wc_rev=1), @@ -1778,7 +1787,9 @@ def merge_into_missing(sbox): expected_skip, None, None, None, None, None, 0, 0, - '--ignore-ancestry', F_path) + '--ignore-ancestry', + '--allow-mixed-revisions', + F_path) # This merge fails when it attempts to descend into the missing # directory. That's OK, there is no real need to support merge into @@ -2691,7 +2702,8 @@ def merge_dir_branches(sbox): ### TODO: We can use run_and_verify_merge() here now. expected_output = expected_merge_output(None, "A " + foo_path + "\n") svntest.actions.run_and_verify_svn(None, expected_output, [], - 'merge', C_url, F_url, wc_dir) + 'merge', '--allow-mixed-revisions', + C_url, F_url, wc_dir) # Run info to check the copied rev to make sure it's right expected_info = {"Path" : re.escape(foo_path), # escape backslashes @@ -3347,7 +3359,10 @@ def merge_file_replace_to_mixed_rev_wc(s expected_elision_output, expected_disk, expected_status, - expected_skip) + expected_skip, + None, None, None, None, None, + True, False, '--allow-mixed-revisions', + wc_dir) # When issue #2522 was filed, svn used to break the WC if we didn't # update here. But nowadays, this no longer happens, so the separate @@ -3433,7 +3448,7 @@ def merge_ignore_whitespace(sbox): expected_status, expected_skip, None, None, None, None, None, - 0, 0, + 0, 0, '--allow-mixed-revisions', '-x', '-w', wc_dir) #---------------------------------------------------------------------- @@ -3515,7 +3530,7 @@ def merge_ignore_eolstyle(sbox): expected_status, expected_skip, None, None, None, None, None, - 0, 0, + 0, 0, '--allow-mixed-revisions', '-x', '--ignore-eol-style', wc_dir) #---------------------------------------------------------------------- @@ -5847,7 +5862,9 @@ def merge_to_path_with_switched_children expected_elision_output, expected_disk, expected_status, expected_skip, - None, None, None, None, None, 1) + None, None, None, None, None, + True, False, '--allow-mixed-revisions', + A_COPY_H_path) # Non-inheritable mergeinfo ranges on a target do prevent repeat # merges on the target itself. @@ -5900,7 +5917,9 @@ def merge_to_path_with_switched_children expected_elision_output, expected_disk_D, expected_status_D, expected_skip_D, - None, None, None, None, None, 1) + None, None, None, None, None, + True, False, '--allow-mixed-revisions', + A_COPY_D_path) # Repeated merge is a no-op, though we still see the notification reporting # the mergeinfo describing the merge has been recorded, though this time it # is a ' G' notification because there is a local mergeinfo change. @@ -5915,7 +5934,9 @@ def merge_to_path_with_switched_children expected_elision_output, expected_disk_D, expected_status_D, expected_skip_D, - None, None, None, None, None, 1) + None, None, None, None, None, + True, False, '--allow-mixed-revisions', + A_COPY_D_path) # Test issue #3187 'Reverse merges don't work properly with # non-inheritable ranges'. @@ -9508,7 +9529,8 @@ def new_subtrees_should_not_break_merge( expected_merge_output([[8]], ['U ' + nu_COPY_path + '\n', ' G ' + nu_COPY_path + '\n']), - [], 'merge', '-c8', sbox.repo_url + '/A/D/H/nu', nu_COPY_path) + [], 'merge', '-c8', '--allow-mixed-revisions', + sbox.repo_url + '/A/D/H/nu', nu_COPY_path) # Merge -r4:6 to A_COPY, then reverse merge r6 from A_COPY/D. expected_output = wc.State(A_COPY_path, { @@ -10028,7 +10050,8 @@ def dont_add_mergeinfo_from_own_history( expected_A_status, expected_A_skip, None, None, None, None, - None, 1) + None, True, False, + '--allow-mixed-revisions', A_path) # Revert all local mods svntest.actions.run_and_verify_svn(None, @@ -10879,7 +10902,9 @@ def reverse_merge_away_all_mergeinfo(sbo expected_elision_output, expected_disk, expected_status, expected_skip, - None, None, None, None, None, 1) + None, None, None, None, None, + True, False, '--allow-mixed-revisions', + A_COPY_H_path) #---------------------------------------------------------------------- # Issue #3138 @@ -12398,10 +12423,11 @@ def svn_commit(path): svn_commit.repo_rev += 1 return svn_commit.repo_rev -def svn_merge(rev_spec, source, target, exp_out=None): +def svn_merge(rev_spec, source, target, exp_out=None, *args): """Merge a single change from path 'source' to path 'target'. SRC_CHANGE_NUM is either a number (to cherry-pick that specific change) - or a command-line option revision range string such as '-r10:20'.""" + or a command-line option revision range string such as '-r10:20'. + *ARGS are additional arguments passed to svn merge.""" source = local_path(source) target = local_path(target) if isinstance(rev_spec, int): @@ -12414,7 +12440,7 @@ def svn_merge(rev_spec, source, target, target_re + ".*':" exp_out = svntest.verify.RegexOutput(exp_1 + "|" + exp_2 + "|" + exp_3) svntest.actions.run_and_verify_svn(None, exp_out, [], - 'merge', rev_spec, source, target) + 'merge', rev_spec, source, target, *args) def svn_propset(pname, pvalue, *paths): "Set property 'pname' to value 'pvalue' on each path in 'paths'" @@ -12939,7 +12965,8 @@ def merge_two_edits_to_same_prop(sbox): % A_path, " U A\n", "Summary of conflicts:\n", - " Property conflicts: 1\n"]) + " Property conflicts: 1\n"], + '--allow-mixed-revisions') # Revert changes to source wc, to test next scenario of #3250 svntest.actions.run_and_verify_svn(None, None, [], @@ -12952,14 +12979,16 @@ def merge_two_edits_to_same_prop(sbox): "--- Recording mergeinfo for merge of r9 into '%s':\n" % A_path, " U A\n", "Summary of conflicts:\n", - " Property conflicts: 1\n"]) + " Property conflicts: 1\n"], + '--allow-mixed-revisions') svn_merge(rev4, A_COPY_path, A_path, [ "--- Merging r10 into '%s':\n" % A_path, " C %s\n" % mu_path, "--- Recording mergeinfo for merge of r10 into '%s':\n" % A_path, " G A\n", "Summary of conflicts:\n", - " Property conflicts: 1\n"]) + " Property conflicts: 1\n"], + '--allow-mixed-revisions') os.chdir(was_cwd) @@ -13004,7 +13033,8 @@ def merge_an_eol_unification_and_set_svn svn_commit('A_COPY') # Merge the two changes together to the target branch. - svn_merge('-r'+str(rev1)+':'+str(rev3), 'A', 'A_COPY') + svn_merge('-r'+str(rev1)+':'+str(rev3), 'A', 'A_COPY', None, + '--allow-mixed-revisions') # That merge should succeed. # Surprise: setting svn:eol-style='LF' instead of 'native' doesn't fail. @@ -15031,6 +15061,7 @@ def merge_automatic_conflict_resolution( list(psi_conflict_support_files), None, None, 1, 1, '--accept', 'postpone', + '--allow-mixed-revisions', A_COPY_path) svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_dir) @@ -15050,6 +15081,7 @@ def merge_automatic_conflict_resolution( None, None, None, None, None, 1, 0, '--accept', 'mine-conflict', + '--allow-mixed-revisions', A_COPY_path) svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_dir) @@ -15064,6 +15096,7 @@ def merge_automatic_conflict_resolution( None, None, None, None, None, 1, 0, '--accept', 'mine-full', + '--allow-mixed-revisions', A_COPY_path) svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_dir) @@ -15083,6 +15116,7 @@ def merge_automatic_conflict_resolution( None, None, None, None, None, 1, 0, '--accept', 'theirs-conflict', + '--allow-mixed-revisions', A_COPY_path) svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_dir) @@ -15097,6 +15131,7 @@ def merge_automatic_conflict_resolution( None, None, None, None, None, 1, 0, '--accept', 'theirs-full', + '--allow-mixed-revisions', A_COPY_path) svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive', wc_dir) @@ -15117,6 +15152,7 @@ def merge_automatic_conflict_resolution( None, None, None, None, None, 1, 0, '--accept', 'base', + '--allow-mixed-revisions', A_COPY_path) #---------------------------------------------------------------------- @@ -15388,7 +15424,8 @@ def committed_case_only_move_and_revert( expected_status, expected_skip, None, None, None, None, - None, 1, 0) + None, 1, 0, + '--allow-mixed-revisions', A_COPY_path) #---------------------------------------------------------------------- # This is a test for issue #3221 'Unable to merge into working copy of @@ -15554,7 +15591,7 @@ def foreign_repos_del_and_props(sbox): svntest.actions.run_and_verify_svn(None, None, [], 'merge', sbox.repo_url, wc2_dir, - '-r', '1:2') + '-r', '1:2', '--allow-mixed-revisions') expected_status.tweak('A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/tau', 'A/D/G/pi', 'A/D/gamma', 'A/D/H', 'A/D/H/psi', 'A/D/H/omega', Modified: subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/merge_tree_conflict_tests.py Wed Oct 27 14:16:37 2010 @@ -416,7 +416,7 @@ def three_way_merge_add_of_existing_bina expected_status, expected_skip, None, None, None, None, None, - 1) + 1, 0, '--allow-mixed-revisions', A_path) #---------------------------------------------------------------------- # Issue #2515 Modified: subversion/trunk/subversion/tests/cmdline/mergeinfo_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/mergeinfo_tests.py?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/mergeinfo_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/mergeinfo_tests.py Wed Oct 27 14:16:37 2010 @@ -412,7 +412,8 @@ def mergeinfo_on_pegged_wc_path(sbox): expected_merge_output([[5]], ['U ' + beta_COPY_path + '\n', ' U ' + A_COPY_path + '\n']), - [], 'merge', '-c5', sbox.repo_url + '/A', A_COPY_path) + [], 'merge', '-c5', '--allow-mixed-revisions', + sbox.repo_url + '/A', A_COPY_path) svntest.actions.run_and_verify_svn(None, None, [], 'ci', wc_dir, '-m', 'Merge r5') Modified: subversion/trunk/subversion/tests/cmdline/resolve_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/resolve_tests.py?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/resolve_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/resolve_tests.py Wed Oct 27 14:16:37 2010 @@ -72,7 +72,7 @@ def automatic_conflict_resolution(sbox): "( U .*A_COPY\n)|" "(Summary of conflicts:\n)|" "( Text conflicts: 1\n)", - [], 'merge', '-c3', + [], 'merge', '-c3', '--allow-mixed-revisions', sbox.repo_url + '/A', A_COPY_path) Modified: subversion/trunk/subversion/tests/cmdline/special_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/special_tests.py?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/special_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/special_tests.py Wed Oct 27 14:16:37 2010 @@ -473,7 +473,7 @@ def merge_file_into_symlink(sbox): # ok, now merge the change to the file into the symlink we created, this # gives us a weird error svntest.main.run_svn(None, - 'merge', '-r', '4:5', d_url, + 'merge', '-r', '4:5', '--allow-mixed-revisions', d_url, os.path.join(wc_dir, 'A', 'Dprime')) # Issue 2701: Tests to see repository with symlinks can be checked out on all Modified: subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py?rev=1027970&r1=1027969&r2=1027970&view=diff ============================================================================== --- subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py (original) +++ subversion/trunk/subversion/tests/cmdline/tree_conflict_tests.py Wed Oct 27 14:16:37 2010 @@ -477,6 +477,7 @@ def ensure_tree_conflict(sbox, operation verbose_print("--- Merging") run_and_verify_svn(None, expected_stdout, [], 'merge', '--ignore-ancestry', + '--allow-mixed-revisions', '-r', str(source_left_rev) + ':' + str(source_right_rev), source_url, target_path) else:
