Hi, merge fans. It's time to lose the temporary '--symmetric' option and make it the default mode in 'svn'.
Some people have been kind enough to try using the 'symmetric' mode and report back, mainly Paul to whom I am most grateful. I am aware of two current issues ([1],[2]) which I am investigating and they are looking simple enough to be resolved soon. Please help me flag up any other issues. Before we consider the details of the UI changes, I thought back to how the user base would see 'symmetric merge' in the context of the history of Subversion's merge tracking, and wrote a brief overview at <http://wiki.apache.org/subversion/SymmetricMerge#The_User.27s_Perspective>. UI CHANGES I think, and please tell me if you disagree, that we will make the best progression with a UI like this, which the attached patch [6] roughly implements for illustration: * Use the 'symmetric'/'auto'[3] mode for any merge that was previously a reintegrate or a non-reintegrate 'sync' merge. That is, when there is: - just one source; - no start revision; - (an end revision may be specified, which can be done without a start rev by specifying peg-rev syntax: SOURCE@END_REV); - no incompatible options (such as --ignore-ancestry). * If the code chooses a reintegrate-like merge, it will apply the strict WC checks like reintegrate does in 1.7. * Make the '--reintegrate' option mean the user believes a reintegrate-style merge is needed, so check that the previous merge is as expected (that it, in the opposite direction) and bail out if not. * Mark the '--reintegrate' option as deprecated, as the reason for its existence has gone. * Delete the '--symmetric' option; it was only there for testing and has not been released. WHAT WILL NOT CHANGE Two specific things to call out: * A merge specifying '--reintegrate' merge will not change in any scenario where 'merge --reintegrate' is the user's best option today. * A merge specifying '-r X:Y' will not change. This requests a cherry-pick merge, using merge tracking to avoid re-merging any revs in the range that are already merged. Currently, if 'X' is 1 or a low enough number such that there are no unmerged revisions before 'X', then Subversion performs exactly the same kind of merge as when 'X' is not specified, which we call a 'sync' merge. I believe we could make a later enhancement to automatically use 'reintegrate' mode if that would be the correct thing to do for merging the requested range. OPTIONS NOT ALLOWED WITH --REINTEGRATE The following options are not allowed with '--reintegrate' in 1.7 but are allowed for non-reintegrate merge-tracking merges: --record-only --depth --force --allow-mixed-revisions My recommendation for these options is that if we choose a reintegrate-style merge then we error out, saying "the required merge is reintegrate-like[4], and option --X is not supported for this kind of merge". Otherwise we handle the option just like in 1.7. Please let me know if this seems good, while I go and adjust the help text. - Julian [1] Unexpected revision number in the "Merging r6 through r9" notification, <http://svn.haxx.se/dev/archive-2012-08/0005.shtml>. [2] Behaviour when the root has not been merged, a subtree has, and now we merge the root in the other direction, <http://svn.haxx.se/dev/archive-2012-08/0008.shtml>. [3] Every time I say 'symmetric', I try to think of a better name. It's not symmetric, it's just based on a symmetric model. 'automatic'? 'bidirectional'? [4] Need to choose terminology for a reintegrate-style merge, for the UI. "reversing direction"? [5] Wiki page, <http://wiki.apache.org/subversion/SymmetricMerge>. [6] Attachment, 'enable-symmetric-merge-1.patch'. [7] GUIs and other clients can switch to the new svn_client API when they are ready to do so; here I'm just talking about our 'svn' client. -- Subversion Live 2012 - 2 Days: training, networking, demos & more! http://bit.ly/NgUaRi Certified & Supported Apache Subversion Downloads: http://www.wandisco.com/subversion/download
Enable symmetric merge as the default merge in 'svn'. If the --reintegrate option is given but a non-reintegrate merge is the kind needed, bail out. Stop recognizing the '--symmetric' option, as that was a temporary development tool. ### This patch should be extended to delete the '--symmetric' option and to ### update the merge help text. * subversion/include/private/svn_client_private.h (svn_client__symmetric_merge_t): Move structure definition to here ... * subversion/libsvn_client/merge.c (svn_client__symmetric_merge_t): ... from here. (do_symmetric_merge_locked): Reject unsupported options in reintegrate mode. * subversion/svn/merge-cmd.c (pathrev_equal): New function. (symmetric_merge): Bail out if a non-reintegrate kind of merge is needed but the '--reintegrate' option was specified. Reject unsupported options in reintegrate mode. (svn_cl__merge): --This line, and those below, will be ignored-- Index: subversion/include/private/svn_client_private.h =================================================================== --- subversion/include/private/svn_client_private.h (revision 1368758) +++ subversion/include/private/svn_client_private.h (working copy) @@ -185,7 +185,10 @@ svn_client__wc_node_get_origin(svn_clien #ifdef SVN_WITH_SYMMETRIC_MERGE /* Details of a symmetric merge. */ -typedef struct svn_client__symmetric_merge_t svn_client__symmetric_merge_t; +typedef struct svn_client__symmetric_merge_t +{ + svn_client__pathrev_t *yca, *base, *mid, *right; +} svn_client__symmetric_merge_t; /* Find the information needed to merge all unmerged changes from a source * branch into a target branch. The information is the locations of the Index: subversion/libsvn_client/merge.c =================================================================== --- subversion/libsvn_client/merge.c (revision 1368758) +++ subversion/libsvn_client/merge.c (working copy) @@ -11149,12 +11149,6 @@ location_on_branch_at_rev(const branch_h return NULL; } -/* Details of a symmetric merge. */ -struct svn_client__symmetric_merge_t -{ - svn_client__pathrev_t *yca, *base, *mid, *right; -}; - /* */ typedef struct source_and_target_t { @@ -11654,6 +11648,24 @@ do_symmetric_merge_locked(const svn_clie svn_ra_session_t *right_ra_session = NULL; svn_ra_session_t *target_ra_session = NULL; + if (record_only) + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("The required merge is reintegrate-like, " + "and the record-only option " + "cannot be used with this kind of merge")); + + if (depth != svn_depth_unknown) + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("The required merge is reintegrate-like, " + "and the depth option " + "cannot be used with this kind of merge")); + + if (force) + return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, + _("The required merge is reintegrate-like, " + "and the force option " + "cannot be used with this kind of merge")); + SVN_ERR(ensure_ra_session_url(&base_ra_session, merge->base->url, ctx, scratch_pool)); SVN_ERR(ensure_ra_session_url(&right_ra_session, merge->right->url, Index: subversion/svn/merge-cmd.c =================================================================== --- subversion/svn/merge-cmd.c (revision 1368758) +++ subversion/svn/merge-cmd.c (working copy) @@ -85,6 +85,15 @@ merge_reintegrate(const char *source_pat return SVN_NO_ERROR; } +/* */ +static svn_boolean_t +pathrev_equal(const svn_client__pathrev_t *pathrev1, + const svn_client__pathrev_t *pathrev2) +{ + return (pathrev1->rev == pathrev2->rev + && strcmp(pathrev1->url, pathrev2->url) == 0); +} + /* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository * revision. */ static svn_error_t * @@ -116,13 +125,15 @@ symmetric_merge(const char *source_path_ svn_boolean_t record_only, svn_boolean_t dry_run, svn_boolean_t allow_mixed_rev, - svn_boolean_t allow_local_mods, - svn_boolean_t allow_switched_subtrees, + svn_boolean_t reintegrate, const apr_array_header_t *merge_options, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { + svn_boolean_t allow_local_mods = ! reintegrate; + svn_boolean_t allow_switched_subtrees = ! reintegrate; svn_client__symmetric_merge_t *merge; + svn_boolean_t reintegrate_like; /* Find the 3-way merges needed (and check suitability of the WC). */ SVN_ERR(svn_client__find_symmetric_merge(&merge, @@ -131,5 +142,41 @@ symmetric_merge(const char *source_path_ allow_local_mods, allow_switched_subtrees, ctx, scratch_pool, scratch_pool)); + reintegrate_like = (merge->mid != NULL); + + if (reintegrate && ! reintegrate_like + && ! pathrev_equal(merge->yca, merge->base)) + { + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("The --reintegrate option was given but the " + "required merge is not reintegrate-like")); + } + if (reintegrate_like) + { + if (record_only) + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("The required merge is reintegrate-like, " + "and the --record-only option " + "cannot be used with this kind of merge")); + + if (depth != svn_depth_unknown) + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("The required merge is reintegrate-like, " + "and the --depth option " + "cannot be used with this kind of merge")); + + if (force) + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("The required merge is reintegrate-like, " + "and the --force option " + "cannot be used with this kind of merge")); + + if (allow_mixed_rev) + return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, + _("The required merge is reintegrate-like, " + "and the --allow-mixed-revisions option " + "cannot be used with this kind of merge")); + } + /* Perform the 3-way merges */ SVN_ERR(svn_client__do_symmetric_merge(merge, target_wcpath, depth, @@ -405,19 +452,11 @@ svn_cl__merge(apr_getopt_t *os, * If any conflicts occur we'll run the conflict resolver later. */ #ifdef SVN_WITH_SYMMETRIC_MERGE - if (opt_state->symmetric_merge) + /* Do a symmetric merge if just one source and no revisions. */ + if ((! two_sources_specified) && (! opt_state->ignore_ancestry) + && first_range_start.kind == svn_opt_revision_unspecified + && first_range_end.kind == svn_opt_revision_unspecified) { - svn_boolean_t allow_local_mods = ! opt_state->reintegrate; - svn_boolean_t allow_switched_subtrees = ! opt_state->reintegrate; - - if (two_sources_specified) - return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("SOURCE2 can't be used with --symmetric")); - if (first_range_start.kind != svn_opt_revision_unspecified - || first_range_end.kind != svn_opt_revision_unspecified) - return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, - _("a revision range can't be used with --symmetric")); - SVN_ERR_W(svn_cl__check_related_source_and_target( sourcepath1, &peg_revision1, targetpath, &unspecified, ctx, pool), @@ -430,8 +469,7 @@ svn_cl__merge(apr_getopt_t *os, opt_state->record_only, opt_state->dry_run, opt_state->allow_mixed_rev, - allow_local_mods, - allow_switched_subtrees, + opt_state->reintegrate, options, ctx, pool); } else