diff options
Diffstat (limited to 'subversion/svn/conflict-callbacks.c')
-rw-r--r-- | subversion/svn/conflict-callbacks.c | 513 |
1 files changed, 292 insertions, 221 deletions
diff --git a/subversion/svn/conflict-callbacks.c b/subversion/svn/conflict-callbacks.c index 0f12413..a9cb39a 100644 --- a/subversion/svn/conflict-callbacks.c +++ b/subversion/svn/conflict-callbacks.c @@ -44,6 +44,9 @@ #include "svn_private_config.h" #define ARRAY_LEN(ary) ((sizeof (ary)) / (sizeof ((ary)[0]))) +#define MAX_ARRAY_LEN(aryx, aryz) \ + (ARRAY_LEN((aryx)) > ARRAY_LEN((aryz)) \ + ? ARRAY_LEN((aryx)) : ARRAY_LEN((aryz))) @@ -56,6 +59,7 @@ struct svn_cl__interactive_conflict_baton_t { const char *path_prefix; svn_boolean_t quit; svn_cl__conflict_stats_t *conflict_stats; + svn_boolean_t printed_summary; }; svn_error_t * @@ -82,6 +86,7 @@ svn_cl__get_conflict_func_interactive_baton( SVN_ERR(svn_dirent_get_absolute(&(*b)->path_prefix, "", result_pool)); (*b)->quit = FALSE; (*b)->conflict_stats = conflict_stats; + (*b)->printed_summary = FALSE; return SVN_NO_ERROR; } @@ -127,6 +132,8 @@ svn_cl__accept_from_word(const char *word) static svn_error_t * show_diff(const svn_wc_conflict_description2_t *desc, const char *path_prefix, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { const char *path1, *path2; @@ -184,11 +191,14 @@ show_diff(const svn_wc_conflict_description2_t *desc, SVN_ERR(svn_stream_for_stdout(&output, pool)); SVN_ERR(svn_diff_file_diff_2(&diff, path1, path2, options, pool)); - return svn_diff_file_output_unified3(output, diff, + return svn_diff_file_output_unified4(output, diff, path1, path2, label1, label2, APR_LOCALE_CHARSET, - NULL, FALSE, + NULL, + options->show_c_function, + options->context_size, + cancel_func, cancel_baton, pool); } @@ -197,6 +207,8 @@ show_diff(const svn_wc_conflict_description2_t *desc, * and 'my' files of DESC. */ static svn_error_t * show_conflicts(const svn_wc_conflict_description2_t *desc, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { svn_diff_t *diff; @@ -213,7 +225,7 @@ show_conflicts(const svn_wc_conflict_description2_t *desc, options, pool)); /* ### Consider putting the markers/labels from ### svn_wc__merge_internal in the conflict description. */ - return svn_diff_file_output_merge2(output, diff, + return svn_diff_file_output_merge3(output, diff, desc->base_abspath, desc->my_abspath, desc->their_abspath, @@ -222,6 +234,8 @@ show_conflicts(const svn_wc_conflict_description2_t *desc, _(">>>>>>> THEIRS (select with 'tc')"), "=======", svn_diff_conflict_display_only_conflicts, + cancel_func, + cancel_baton, pool); } @@ -237,6 +251,8 @@ static svn_error_t * merge_prop_conflict(svn_stream_t *output, const svn_wc_conflict_description2_t *desc, const char *merged_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { const char *base_abspath = desc->base_abspath; @@ -268,7 +284,7 @@ merge_prop_conflict(svn_stream_t *output, merged_abspath ? merged_abspath : my_abspath, their_abspath, options, pool)); - SVN_ERR(svn_diff_file_output_merge2(output, diff, + SVN_ERR(svn_diff_file_output_merge3(output, diff, base_abspath, merged_abspath ? merged_abspath : my_abspath, @@ -278,6 +294,8 @@ merge_prop_conflict(svn_stream_t *output, _(">>>>>>> THEIRS"), "=======", svn_diff_conflict_display_modified_original_latest, + cancel_func, + cancel_baton, pool)); return SVN_NO_ERROR; @@ -293,12 +311,15 @@ merge_prop_conflict(svn_stream_t *output, static svn_error_t * show_prop_conflict(const svn_wc_conflict_description2_t *desc, const char *merged_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { svn_stream_t *output; SVN_ERR(svn_stream_for_stdout(&output, pool)); - SVN_ERR(merge_prop_conflict(output, desc, merged_abspath, pool)); + SVN_ERR(merge_prop_conflict(output, desc, merged_abspath, + cancel_func, cancel_baton, pool)); return SVN_NO_ERROR; } @@ -324,22 +345,14 @@ open_editor(svn_boolean_t *performed_edit, { err = svn_cmdline__edit_file_externally(merged_file, b->editor_cmd, b->config, pool); - if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR)) + if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR || + err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) { - svn_error_t *root_err = svn_error_root_cause(err); + char buf[1024]; + const char *message; - SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n", - root_err->message ? root_err->message : - _("No editor found."))); - svn_error_clear(err); - } - else if (err && (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) - { - svn_error_t *root_err = svn_error_root_cause(err); - - SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n", - root_err->message ? root_err->message : - _("Error running editor."))); + message = svn_err_best_message(err, buf, sizeof(buf)); + SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n", message)); svn_error_clear(err); } else if (err) @@ -375,60 +388,18 @@ edit_prop_conflict(const char **merged_file_path, result_pool, scratch_pool)); merged_prop = svn_stream_from_aprfile2(file, TRUE /* disown */, scratch_pool); - SVN_ERR(merge_prop_conflict(merged_prop, desc, NULL, scratch_pool)); + SVN_ERR(merge_prop_conflict(merged_prop, desc, NULL, + b->pb->cancel_func, + b->pb->cancel_baton, + scratch_pool)); SVN_ERR(svn_stream_close(merged_prop)); - SVN_ERR(svn_io_file_flush_to_disk(file, scratch_pool)); + SVN_ERR(svn_io_file_flush(file, scratch_pool)); SVN_ERR(open_editor(&performed_edit, file_path, b, scratch_pool)); *merged_file_path = (performed_edit ? file_path : NULL); return SVN_NO_ERROR; } -/* Run an external merge tool, passing it the 'base', 'their', 'my' and - * 'merged' files in DESC. The tool to use is determined by B->config and - * environment variables; see svn_cl__merge_file_externally() for details. - * - * If the tool runs, set *PERFORMED_EDIT to true; if a tool is not - * configured or cannot run, do not touch *PERFORMED_EDIT, report the error - * on stderr, and return SVN_NO_ERROR; if any other error is encountered, - * return that error. */ -static svn_error_t * -launch_resolver(svn_boolean_t *performed_edit, - const svn_wc_conflict_description2_t *desc, - svn_cl__interactive_conflict_baton_t *b, - apr_pool_t *pool) -{ - svn_error_t *err; - - err = svn_cl__merge_file_externally(desc->base_abspath, desc->their_abspath, - desc->my_abspath, desc->merged_file, - desc->local_abspath, b->config, NULL, - pool); - if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL) - { - SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n", - err->message ? err->message : - _("No merge tool found, " - "try '(m) merge' instead.\n"))); - svn_error_clear(err); - } - else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM) - { - SVN_ERR(svn_cmdline_fprintf(stderr, pool, "%s\n", - err->message ? err->message : - _("Error running merge tool, " - "try '(m) merge' instead."))); - svn_error_clear(err); - } - else if (err) - return svn_error_trace(err); - else if (performed_edit) - *performed_edit = TRUE; - - return SVN_NO_ERROR; -} - - /* Maximum line length for the prompt string. */ #define MAX_PROMPT_WIDTH 70 @@ -438,7 +409,8 @@ typedef struct resolver_option_t const char *code; /* one or two characters */ const char *short_desc; /* label in prompt (localized) */ const char *long_desc; /* longer description (localized) */ - svn_wc_conflict_choice_t choice; /* or -1 if not a simple choice */ + svn_wc_conflict_choice_t choice; + /* or ..._undefined if not a simple choice */ } resolver_option_t; /* Resolver options for a text conflict */ @@ -450,14 +422,15 @@ static const resolver_option_t text_conflict_options[] = brackets. */ { "e", N_("edit file"), N_("change merged file in an editor" " [edit]"), - -1 }, + svn_wc_conflict_choose_undefined }, { "df", N_("show diff"), N_("show all changes made to merged file"), - -1 }, - { "r", N_("mark resolved"), N_("accept merged version of file"), + svn_wc_conflict_choose_undefined }, + { "r", N_("mark resolved"), N_("accept merged version of file [working]"), svn_wc_conflict_choose_merged }, { "", "", "", svn_wc_conflict_choose_unspecified }, { "dc", N_("display conflict"), N_("show all conflicts " - "(ignoring merged version)"), -1 }, + "(ignoring merged version)"), + svn_wc_conflict_choose_undefined }, { "mc", N_("my side of conflict"), N_("accept my version for all conflicts " "(same) [mine-conflict]"), svn_wc_conflict_choose_mine_conflict }, @@ -473,16 +446,43 @@ static const resolver_option_t text_conflict_options[] = "(same) [theirs-full]"), svn_wc_conflict_choose_theirs_full }, { "", "", "", svn_wc_conflict_choose_unspecified }, - { "m", N_("merge"), N_("use internal merge tool to resolve " - "conflict"), -1 }, - { "l", N_("launch tool"), N_("launch external tool to resolve " - "conflict [launch]"), -1 }, + { "m", N_("merge"), N_("use merge tool to resolve conflict"), + svn_wc_conflict_choose_undefined }, + { "l", N_("launch tool"), N_("launch external merge tool to resolve " + "conflict [launch]"), + svn_wc_conflict_choose_undefined }, + { "i", N_("internal merge tool"), N_("use built-in merge tool to " + "resolve conflict"), + svn_wc_conflict_choose_undefined }, { "p", N_("postpone"), N_("mark the conflict to be resolved later" " [postpone]"), svn_wc_conflict_choose_postpone }, { "q", N_("quit resolution"), N_("postpone all remaining conflicts"), svn_wc_conflict_choose_postpone }, - { "s", N_("show all options"), N_("show this list (also 'h', '?')"), -1 }, + { "s", N_("show all options"), N_("show this list (also 'h', '?')"), + svn_wc_conflict_choose_undefined }, + { NULL } +}; + +/* Resolver options for a binary file conflict. */ +static const resolver_option_t binary_conflict_options[] = +{ + /* Translators: keep long_desc below 70 characters (wrap with a left + margin of 9 spaces if needed); don't translate the words within square + brackets. */ + { "r", N_("mark resolved"), N_("accept the working copy version of file " + " [working]"), + svn_wc_conflict_choose_merged }, + { "tf", N_("their version"), N_("accept the incoming version of file " + " [theirs-full]"), + svn_wc_conflict_choose_theirs_full }, + { "p", N_("postpone"), N_("mark the conflict to be resolved later " + " [postpone]"), + svn_wc_conflict_choose_postpone }, + { "q", N_("quit resolution"), N_("postpone all remaining conflicts"), + svn_wc_conflict_choose_postpone }, + { "s", N_("show all options"), N_("show this list (also 'h', '?')"), + svn_wc_conflict_choose_undefined }, { NULL } }; @@ -495,9 +495,11 @@ static const resolver_option_t prop_conflict_options[] = { "tf", N_("their version"), N_("accept their version of entire property " "(same) [theirs-full]"), svn_wc_conflict_choose_theirs_full }, - { "dc", N_("display conflict"), N_("show conflicts in this property"), -1 }, + { "dc", N_("display conflict"), N_("show conflicts in this property"), + svn_wc_conflict_choose_undefined }, { "e", N_("edit property"), N_("change merged property value in an editor" - " [edit]"), -1 }, + " [edit]"), + svn_wc_conflict_choose_undefined }, { "r", N_("mark resolved"), N_("accept edited version of property"), svn_wc_conflict_choose_merged }, { "p", N_("postpone"), N_("mark the conflict to be resolved later" @@ -505,7 +507,8 @@ static const resolver_option_t prop_conflict_options[] = svn_wc_conflict_choose_postpone }, { "q", N_("quit resolution"), N_("postpone all remaining conflicts"), svn_wc_conflict_choose_postpone }, - { "h", N_("help"), N_("show this help (also '?')"), -1 }, + { "h", N_("help"), N_("show this help (also '?')"), + svn_wc_conflict_choose_undefined }, { NULL } }; @@ -518,31 +521,14 @@ static const resolver_option_t tree_conflict_options[] = svn_wc_conflict_choose_postpone }, { "q", N_("quit resolution"), N_("postpone all remaining conflicts"), svn_wc_conflict_choose_postpone }, - { "h", N_("help"), N_("show this help (also '?')"), -1 }, + { "h", N_("help"), N_("show this help (also '?')"), + svn_wc_conflict_choose_undefined }, { NULL } }; static const resolver_option_t tree_conflict_options_update_moved_away[] = { - { "mc", N_("apply update (recommended)"), - N_("apply update to the move destination" - " [mine-conflict]"), - svn_wc_conflict_choose_mine_conflict }, - { "r", N_("discard update (breaks move)"), N_("discard update, mark " - "resolved, the move will " - "will become a copy"), - svn_wc_conflict_choose_merged }, - { "p", N_("postpone"), N_("resolve the conflict later [postpone]"), - svn_wc_conflict_choose_postpone }, - { "q", N_("quit resolution"), N_("postpone all remaining conflicts"), - svn_wc_conflict_choose_postpone }, - { "h", N_("help"), N_("show this help (also '?')"), -1 }, - { NULL } -}; - -static const resolver_option_t tree_conflict_options_update_edit_moved_away[] = -{ - { "mc", N_("apply update to move destination"), + { "mc", N_("apply update to move destination (recommended)"), N_("apply incoming update to move destination" " [mine-conflict]"), svn_wc_conflict_choose_mine_conflict }, @@ -550,43 +536,26 @@ static const resolver_option_t tree_conflict_options_update_edit_moved_away[] = svn_wc_conflict_choose_postpone }, { "q", N_("quit resolution"), N_("postpone all remaining conflicts"), svn_wc_conflict_choose_postpone }, - { "h", N_("help"), N_("show this help (also '?')"), -1 }, - { NULL } -}; - -static const resolver_option_t tree_conflict_options_update_deleted[] = -{ - { "mc", N_("keep affected local moves"), N_("keep any local moves affected " - "by this deletion [mine-conflict]"), - svn_wc_conflict_choose_mine_conflict }, - { "r", N_("mark resolved (breaks moves)"), N_("mark resolved, any affected " - "moves will become copies"), - svn_wc_conflict_choose_merged }, - { "p", N_("postpone"), N_("resolve the conflict later [postpone]"), - svn_wc_conflict_choose_postpone }, - { "q", N_("quit resolution"), N_("postpone all remaining conflicts"), - svn_wc_conflict_choose_postpone }, - { "h", N_("help"), N_("show this help (also '?')"), -1 }, + { "h", N_("help"), N_("show this help (also '?')"), + svn_wc_conflict_choose_undefined }, { NULL } }; -static const resolver_option_t tree_conflict_options_update_replaced[] = +static const resolver_option_t tree_conflict_options_update_edit_deleted_dir[] = { - { "mc", N_("keep affected local moves"), N_("keep any moves affected by this " - "replacement [mine-conflict]"), + { "mc", N_("prepare for updating moved-away children, if any (recommended)"), + N_("allow updating moved-away children " + "with 'svn resolve' [mine-conflict]"), svn_wc_conflict_choose_mine_conflict }, - { "r", N_("mark resolved (breaks moves)"), N_("mark resolved (any affected " - "moves will become copies)"), - svn_wc_conflict_choose_merged }, { "p", N_("postpone"), N_("resolve the conflict later [postpone]"), svn_wc_conflict_choose_postpone }, { "q", N_("quit resolution"), N_("postpone all remaining conflicts"), svn_wc_conflict_choose_postpone }, - { "h", N_("help"), N_("show this help (also '?')"), -1 }, + { "h", N_("help"), N_("show this help (also '?')"), + svn_wc_conflict_choose_undefined }, { NULL } }; - /* Return a pointer to the option description in OPTIONS matching the * one- or two-character OPTION_CODE. Return NULL if not found. */ static const resolver_option_t * @@ -637,21 +606,21 @@ prompt_string(const resolver_option_t *options, } if (! first) - result = apr_pstrcat(pool, result, ",", (char *)NULL); + result = apr_pstrcat(pool, result, ",", SVN_VA_NULL); s = apr_psprintf(pool, _(" (%s) %s"), opt->code, _(opt->short_desc)); slen = svn_utf_cstring_utf8_width(s); /* Break the line if adding the next option would make it too long */ if (this_line_len + slen > MAX_PROMPT_WIDTH) { - result = apr_pstrcat(pool, result, line_sep, (char *)NULL); + result = apr_pstrcat(pool, result, line_sep, SVN_VA_NULL); this_line_len = left_margin; } - result = apr_pstrcat(pool, result, s, (char *)NULL); + result = apr_pstrcat(pool, result, s, SVN_VA_NULL); this_line_len += slen; first = FALSE; } - return apr_pstrcat(pool, result, ": ", (char *)NULL); + return apr_pstrcat(pool, result, ": ", SVN_VA_NULL); } /* Return a help string listing the OPTIONS. */ @@ -674,13 +643,13 @@ help_string(const resolver_option_t *options, } else { - result = apr_pstrcat(pool, result, "\n", (char *)NULL); + result = apr_pstrcat(pool, result, "\n", SVN_VA_NULL); } } result = apr_pstrcat(pool, result, _("Words in square brackets are the corresponding " "--accept option arguments.\n"), - (char *)NULL); + SVN_VA_NULL); return result; } @@ -736,31 +705,50 @@ handle_text_conflict(svn_wc_conflict_result_t *result, apr_pool_t *iterpool = svn_pool_create(scratch_pool); svn_boolean_t diff_allowed = FALSE; /* Have they done something that might have affected the merged - file (so that we need to save a .edited copy)? */ + file (so that we need to save a .edited copy by setting the + result->save_merge flag)? */ svn_boolean_t performed_edit = FALSE; /* Have they done *something* (edit, look at diff, etc) to give them a rational basis for choosing (r)esolved? */ svn_boolean_t knows_something = FALSE; + const char *local_relpath; SVN_ERR_ASSERT(desc->kind == svn_wc_conflict_kind_text); - SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, - _("Conflict discovered in file '%s'.\n"), - svn_cl__local_style_skip_ancestor( - b->path_prefix, desc->local_abspath, - scratch_pool))); + local_relpath = svn_cl__local_style_skip_ancestor(b->path_prefix, + desc->local_abspath, + scratch_pool);; + + if (desc->is_binary) + SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, + _("Conflict discovered in binary file '%s'.\n"), + local_relpath)); + else + SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, + _("Conflict discovered in file '%s'.\n"), + local_relpath)); + + /* ### TODO This whole feature availability check is grossly outdated. + DIFF_ALLOWED needs either to be redefined or to go away. + */ /* Diffing can happen between base and merged, to show conflict markers to the user (this is the typical 3-way merge scenario), or if no base is available, we can show a diff between mine and theirs. */ - if ((desc->merged_file && desc->base_abspath) - || (!desc->base_abspath && desc->my_abspath && desc->their_abspath)) + if (!desc->is_binary && + ((desc->merged_file && desc->base_abspath) + || (!desc->base_abspath && desc->my_abspath && desc->their_abspath))) diff_allowed = TRUE; while (TRUE) { - const char *options[ARRAY_LEN(text_conflict_options)]; + const char *options[1 + MAX_ARRAY_LEN(binary_conflict_options, + text_conflict_options)]; + + const resolver_option_t *conflict_options = desc->is_binary + ? binary_conflict_options + : text_conflict_options; const char **next_option = options; const resolver_option_t *opt; @@ -769,31 +757,39 @@ handle_text_conflict(svn_wc_conflict_result_t *result, *next_option++ = "p"; if (diff_allowed) { - *next_option++ = "df"; + /* We need one more path for this feature. */ + if (desc->my_abspath) + *next_option++ = "df"; + *next_option++ = "e"; - *next_option++ = "m"; + + /* We need one more path for this feature. */ + if (desc->my_abspath) + *next_option++ = "m"; if (knows_something) *next_option++ = "r"; - if (! desc->is_binary) - { - *next_option++ = "mc"; - *next_option++ = "tc"; - } + *next_option++ = "mc"; + *next_option++ = "tc"; } else { - if (knows_something) + if (knows_something || desc->is_binary) *next_option++ = "r"; - *next_option++ = "mf"; + + /* The 'mine-full' option selects the ".mine" file so only offer + * it if that file exists. It does not exist for binary files, + * for example (questionable historical behaviour since 1.0). */ + if (desc->my_abspath) + *next_option++ = "mf"; + *next_option++ = "tf"; } *next_option++ = "s"; *next_option++ = NULL; - SVN_ERR(prompt_user(&opt, text_conflict_options, options, b->pb, - iterpool)); + SVN_ERR(prompt_user(&opt, conflict_options, options, b->pb, iterpool)); if (! opt) continue; @@ -807,7 +803,7 @@ handle_text_conflict(svn_wc_conflict_result_t *result, else if (strcmp(opt->code, "s") == 0) { SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n", - help_string(text_conflict_options, + help_string(conflict_options, iterpool))); } else if (strcmp(opt->code, "dc") == 0) @@ -828,12 +824,16 @@ handle_text_conflict(svn_wc_conflict_result_t *result, "files not available.\n\n"))); continue; } - SVN_ERR(show_conflicts(desc, iterpool)); + SVN_ERR(show_conflicts(desc, + b->pb->cancel_func, + b->pb->cancel_baton, + iterpool)); knows_something = TRUE; } else if (strcmp(opt->code, "df") == 0) { - if (! diff_allowed) + /* Re-check preconditions. */ + if (! diff_allowed || ! desc->my_abspath) { SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, _("Invalid option; there's no " @@ -841,7 +841,9 @@ handle_text_conflict(svn_wc_conflict_result_t *result, continue; } - SVN_ERR(show_diff(desc, b->path_prefix, iterpool)); + SVN_ERR(show_diff(desc, b->path_prefix, + b->pb->cancel_func, b->pb->cancel_baton, + iterpool)); knows_something = TRUE; } else if (strcmp(opt->code, "e") == 0 || strcmp(opt->code, ":-E") == 0) @@ -853,36 +855,68 @@ handle_text_conflict(svn_wc_conflict_result_t *result, else if (strcmp(opt->code, "m") == 0 || strcmp(opt->code, ":-g") == 0 || strcmp(opt->code, "=>-") == 0 || strcmp(opt->code, ":>.") == 0) { - if (desc->kind != svn_wc_conflict_kind_text) + svn_error_t *err; + + /* Re-check preconditions. */ + if (! desc->my_abspath) { SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, - _("Invalid option; can only " - "resolve text conflicts with " - "the internal merge tool." - "\n\n"))); + _("Invalid option; there's no " + "base path to merge.\n\n"))); continue; } - if (desc->base_abspath && desc->their_abspath && - desc->my_abspath && desc->merged_file) + err = svn_cl__merge_file_externally(desc->base_abspath, + desc->their_abspath, + desc->my_abspath, + desc->merged_file, + desc->local_abspath, b->config, + NULL, iterpool); + if (err) { - svn_boolean_t remains_in_conflict; - - SVN_ERR(svn_cl__merge_file(desc->base_abspath, - desc->their_abspath, - desc->my_abspath, - desc->merged_file, - desc->local_abspath, - b->path_prefix, - b->editor_cmd, - b->config, - &remains_in_conflict, - iterpool)); - knows_something = !remains_in_conflict; + if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL) + { + svn_boolean_t remains_in_conflict = TRUE; + + /* Try the internal merge tool. */ + svn_error_clear(err); + SVN_ERR(svn_cl__merge_file(&remains_in_conflict, + desc->base_abspath, + desc->their_abspath, + desc->my_abspath, + desc->merged_file, + desc->local_abspath, + b->path_prefix, + b->editor_cmd, + b->config, + b->pb->cancel_func, + b->pb->cancel_baton, + iterpool)); + knows_something = !remains_in_conflict; + } + else if (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM) + { + char buf[1024]; + const char *message; + + message = svn_err_best_message(err, buf, sizeof(buf)); + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, + "%s\n", message)); + svn_error_clear(err); + continue; + } + else + return svn_error_trace(err); } else - SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, - _("Invalid option.\n\n"))); + { + /* The external merge tool's exit code was either 0 or 1. + * The tool may leave the file conflicted by exiting with + * exit code 1, and we allow the user to mark the conflict + * resolved in this case. */ + performed_edit = TRUE; + knows_something = TRUE; + } } else if (strcmp(opt->code, "l") == 0 || strcmp(opt->code, ":-l") == 0) { @@ -893,7 +927,29 @@ handle_text_conflict(svn_wc_conflict_result_t *result, if (desc->base_abspath && desc->their_abspath && desc->my_abspath && desc->merged_file) { - SVN_ERR(launch_resolver(&performed_edit, desc, b, iterpool)); + svn_error_t *err; + char buf[1024]; + const char *message; + + err = svn_cl__merge_file_externally(desc->base_abspath, + desc->their_abspath, + desc->my_abspath, + desc->merged_file, + desc->local_abspath, + b->config, NULL, iterpool); + if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL || + err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) + { + message = svn_err_best_message(err, buf, sizeof(buf)); + SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, "%s\n", + message)); + svn_error_clear(err); + } + else if (err) + return svn_error_trace(err); + else + performed_edit = TRUE; + if (performed_edit) knows_something = TRUE; } @@ -901,7 +957,27 @@ handle_text_conflict(svn_wc_conflict_result_t *result, SVN_ERR(svn_cmdline_fprintf(stderr, iterpool, _("Invalid option.\n\n"))); } - else if (opt->choice != -1) + else if (strcmp(opt->code, "i") == 0) + { + svn_boolean_t remains_in_conflict = TRUE; + + SVN_ERR(svn_cl__merge_file(&remains_in_conflict, + desc->base_abspath, + desc->their_abspath, + desc->my_abspath, + desc->merged_file, + desc->local_abspath, + b->path_prefix, + b->editor_cmd, + b->config, + b->pb->cancel_func, + b->pb->cancel_baton, + iterpool)); + + if (!remains_in_conflict) + knows_something = TRUE; + } + else if (opt->choice != svn_wc_conflict_choose_undefined) { if ((opt->choice == svn_wc_conflict_choose_mine_conflict || opt->choice == svn_wc_conflict_choose_theirs_conflict) @@ -918,7 +994,7 @@ handle_text_conflict(svn_wc_conflict_result_t *result, the file if they've edited it, or at least looked at the diff. */ if (opt->choice == svn_wc_conflict_choose_merged - && ! knows_something) + && ! knows_something && diff_allowed) { SVN_ERR(svn_cmdline_fprintf( stderr, iterpool, @@ -1008,7 +1084,9 @@ handle_prop_conflict(svn_wc_conflict_result_t *result, } else if (strcmp(opt->code, "dc") == 0) { - SVN_ERR(show_prop_conflict(desc, merged_file_path, scratch_pool)); + SVN_ERR(show_prop_conflict(desc, merged_file_path, + b->pb->cancel_func, b->pb->cancel_baton, + scratch_pool)); } else if (strcmp(opt->code, "e") == 0) { @@ -1030,7 +1108,7 @@ handle_prop_conflict(svn_wc_conflict_result_t *result, result->choice = svn_wc_conflict_choose_merged; break; } - else if (opt->choice != -1) + else if (opt->choice != svn_wc_conflict_choose_undefined) { result->choice = opt->choice; break; @@ -1072,25 +1150,23 @@ handle_tree_conflict(svn_wc_conflict_result_t *result, svn_pool_clear(iterpool); + tc_opts = tree_conflict_options; + if (desc->operation == svn_wc_operation_update || desc->operation == svn_wc_operation_switch) { if (desc->reason == svn_wc_conflict_reason_moved_away) { - if (desc->action == svn_wc_conflict_action_edit) - tc_opts = tree_conflict_options_update_edit_moved_away; - else - tc_opts = tree_conflict_options_update_moved_away; + tc_opts = tree_conflict_options_update_moved_away; + } + else if (desc->reason == svn_wc_conflict_reason_deleted || + desc->reason == svn_wc_conflict_reason_replaced) + { + if (desc->action == svn_wc_conflict_action_edit && + desc->node_kind == svn_node_dir) + tc_opts = tree_conflict_options_update_edit_deleted_dir; } - else if (desc->reason == svn_wc_conflict_reason_deleted) - tc_opts = tree_conflict_options_update_deleted; - else if (desc->reason == svn_wc_conflict_reason_replaced) - tc_opts = tree_conflict_options_update_replaced; - else - tc_opts = tree_conflict_options; } - else - tc_opts = tree_conflict_options; SVN_ERR(prompt_user(&opt, tc_opts, NULL, b->pb, iterpool)); if (! opt) @@ -1103,7 +1179,7 @@ handle_tree_conflict(svn_wc_conflict_result_t *result, b->quit = TRUE; break; } - else if (opt->choice != -1) + else if (opt->choice != svn_wc_conflict_choose_undefined) { result->choice = opt->choice; break; @@ -1172,21 +1248,15 @@ conflict_func_interactive(svn_wc_conflict_result_t **result, err = svn_cmdline__edit_file_externally(desc->merged_file, b->editor_cmd, b->config, scratch_pool); - if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR)) - { - SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", - err->message ? err->message : - _("No editor found;" - " leaving all conflicts."))); - svn_error_clear(err); - b->external_failed = TRUE; - } - else if (err && (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) + if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR || + err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) { + char buf[1024]; + const char *message; + + message = svn_err_best_message(err, buf, sizeof(buf)); SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", - err->message ? err->message : - _("Error running editor;" - " leaving all conflicts."))); + message)); svn_error_clear(err); b->external_failed = TRUE; } @@ -1217,21 +1287,15 @@ conflict_func_interactive(svn_wc_conflict_result_t **result, b->config, &remains_in_conflict, scratch_pool); - if (err && err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL) - { - SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", - err->message ? err->message : - _("No merge tool found;" - " leaving all conflicts."))); - b->external_failed = TRUE; - return svn_error_trace(err); - } - else if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM) + if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL || + err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)) { + char buf[1024]; + const char *message; + + message = svn_err_best_message(err, buf, sizeof(buf)); SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", - err->message ? err->message : - _("Error running merge tool;" - " leaving all conflicts."))); + message)); b->external_failed = TRUE; return svn_error_trace(err); } @@ -1248,6 +1312,13 @@ conflict_func_interactive(svn_wc_conflict_result_t **result, break; } + /* Print a summary of conflicts before starting interactive resolution */ + if (! b->printed_summary) + { + SVN_ERR(svn_cl__print_conflict_stats(b->conflict_stats, scratch_pool)); + b->printed_summary = TRUE; + } + /* We're in interactive mode and either the user gave no --accept option or the option did not apply; let's prompt. */ @@ -1293,7 +1364,7 @@ svn_cl__conflict_func_interactive(svn_wc_conflict_result_t **result, b->path_prefix, desc->local_abspath, scratch_pool); svn_cl__conflict_stats_resolved(b->conflict_stats, local_path, - desc->kind); + desc->kind); } return SVN_NO_ERROR; } |