summaryrefslogtreecommitdiff
path: root/subversion/svn/conflict-callbacks.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svn/conflict-callbacks.c')
-rw-r--r--subversion/svn/conflict-callbacks.c513
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;
}