summaryrefslogtreecommitdiff
path: root/subversion/svn/merge-cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svn/merge-cmd.c')
-rw-r--r--subversion/svn/merge-cmd.c123
1 files changed, 123 insertions, 0 deletions
diff --git a/subversion/svn/merge-cmd.c b/subversion/svn/merge-cmd.c
index cbc818b89f7cc..f5c19198a5b29 100644
--- a/subversion/svn/merge-cmd.c
+++ b/subversion/svn/merge-cmd.c
@@ -150,6 +150,85 @@ run_merge(svn_boolean_t two_sources_specified,
return merge_err;
}
+/* Baton type for conflict_func_merge_cmd(). */
+struct conflict_func_merge_cmd_baton {
+ svn_cl__accept_t accept_which;
+ const char *path_prefix;
+ svn_cl__conflict_stats_t *conflict_stats;
+};
+
+/* This implements the `svn_wc_conflict_resolver_func2_t ' interface.
+ *
+ * The merge subcommand needs to install this legacy conflict callback
+ * in case the user passed an --accept option to 'svn merge'.
+ * Otherwise, merges involving multiple editor drives might encounter a
+ * conflict during one of the editor drives and abort with an error,
+ * rather than resolving conflicts as per the --accept option and
+ * continuing with the next editor drive.
+ * ### TODO add an svn_client_merge API that makes this callback unnecessary
+ */
+static svn_error_t *
+conflict_func_merge_cmd(svn_wc_conflict_result_t **result,
+ const svn_wc_conflict_description2_t *desc,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct conflict_func_merge_cmd_baton *b = baton;
+ svn_wc_conflict_choice_t choice;
+
+ switch (b->accept_which)
+ {
+ case svn_cl__accept_postpone:
+ case svn_cl__accept_invalid:
+ case svn_cl__accept_unspecified:
+ case svn_cl__accept_recommended:
+ /* Postpone or no valid --accept option, postpone the conflict. */
+ choice = svn_wc_conflict_choose_postpone;
+ break;
+ case svn_cl__accept_base:
+ choice = svn_wc_conflict_choose_base;
+ break;
+ case svn_cl__accept_working:
+ choice = svn_wc_conflict_choose_merged;
+ break;
+ case svn_cl__accept_mine_conflict:
+ choice = svn_wc_conflict_choose_mine_conflict;
+ break;
+ case svn_cl__accept_theirs_conflict:
+ choice = svn_wc_conflict_choose_theirs_conflict;
+ break;
+ case svn_cl__accept_mine_full:
+ choice = svn_wc_conflict_choose_mine_full;
+ break;
+ case svn_cl__accept_theirs_full:
+ choice = svn_wc_conflict_choose_theirs_full;
+ break;
+ case svn_cl__accept_edit:
+ case svn_cl__accept_launch:
+ /* The 'edit' and 'launch' options used to be valid in Subversion 1.9 but
+ * we can't support these options for the purposes of this callback. */
+ choice = svn_wc_conflict_choose_postpone;
+ break;
+ }
+
+ *result = svn_wc_create_conflict_result(choice, NULL, result_pool);
+
+ /* If we are resolving a conflict, adjust the summary of conflicts. */
+ if (choice != svn_wc_conflict_choose_postpone)
+ {
+ const char *local_path;
+
+ local_path = svn_cl__local_style_skip_ancestor(b->path_prefix,
+ desc->local_abspath,
+ scratch_pool);
+ svn_cl__conflict_stats_resolved(b->conflict_stats, local_path,
+ desc->kind);
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__merge(apr_getopt_t *os,
@@ -157,6 +236,8 @@ svn_cl__merge(apr_getopt_t *os,
apr_pool_t *pool)
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_cl__conflict_stats_t *conflict_stats =
+ ((svn_cl__cmd_baton_t *) baton)->conflict_stats;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
apr_array_header_t *targets;
const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = "";
@@ -165,6 +246,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;
+ apr_array_header_t *conflicted_paths;
svn_boolean_t has_explicit_target = FALSE;
/* Merge doesn't support specifying a revision or revision range
@@ -432,6 +514,22 @@ svn_cl__merge(apr_getopt_t *os,
"with --reintegrate"));
}
+ /* Install a legacy conflict handler if the --accept option was given.
+ * Else, svn_client_merge5() may abort the merge in an undesirable way.
+ * See the docstring at conflict_func_merge_cmd() for details */
+ if (opt_state->accept_which != svn_cl__accept_unspecified)
+ {
+ struct conflict_func_merge_cmd_baton *b = apr_pcalloc(pool, sizeof(*b));
+
+ b->accept_which = opt_state->accept_which;
+ SVN_ERR(svn_dirent_get_absolute(&b->path_prefix, "", pool));
+ b->conflict_stats = conflict_stats;
+
+ ctx->conflict_func2 = conflict_func_merge_cmd;
+ ctx->conflict_baton2 = b;
+ }
+
+retry:
merge_err = run_merge(two_sources_specified,
sourcepath1, peg_revision1,
sourcepath2,
@@ -447,6 +545,31 @@ svn_cl__merge(apr_getopt_t *os,
"fix invalid mergeinfo in target with 'svn propset'"));
}
+ /* Run the interactive resolver if conflicts were raised. */
+ SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
+ pool, pool));
+ if (conflicted_paths)
+ {
+ SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
+ opt_state, ctx, pool));
+ if (merge_err &&
+ svn_error_root_cause(merge_err)->apr_err == SVN_ERR_WC_FOUND_CONFLICT)
+ {
+ svn_error_t *err;
+
+ /* Check if all conflicts were resolved just now. */
+ err = svn_cl__conflict_stats_get_paths(&conflicted_paths,
+ conflict_stats, pool, pool);
+ if (err)
+ merge_err = svn_error_compose_create(merge_err, err);
+ else if (conflicted_paths == NULL)
+ {
+ svn_error_clear(merge_err);
+ goto retry; /* ### conflicts resolved; continue merging */
+ }
+ }
+ }
+
if (!opt_state->quiet)
{
svn_error_t *err = svn_cl__notifier_print_conflict_stats(