summaryrefslogtreecommitdiff
path: root/subversion/libsvn_wc/conflicts.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_wc/conflicts.c')
-rw-r--r--subversion/libsvn_wc/conflicts.c752
1 files changed, 682 insertions, 70 deletions
diff --git a/subversion/libsvn_wc/conflicts.c b/subversion/libsvn_wc/conflicts.c
index f04c6de59481c..606710c69f001 100644
--- a/subversion/libsvn_wc/conflicts.c
+++ b/subversion/libsvn_wc/conflicts.c
@@ -50,6 +50,7 @@
#include "private/svn_wc_private.h"
#include "private/svn_skel.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_string_private.h"
#include "svn_private_config.h"
@@ -1351,8 +1352,6 @@ generate_propconflict(svn_boolean_t *conflict_remains,
}
case svn_wc_conflict_choose_merged:
{
- svn_stringbuf_t *merged_stringbuf;
-
if (!cdesc->merged_file
&& (!result->merged_file && !result->merged_value))
return svn_error_create
@@ -1364,6 +1363,8 @@ generate_propconflict(svn_boolean_t *conflict_remains,
new_value = result->merged_value;
else
{
+ svn_stringbuf_t *merged_stringbuf;
+
SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf,
result->merged_file ?
result->merged_file :
@@ -2339,6 +2340,17 @@ svn_wc__read_conflicts(const apr_array_header_t **conflicts,
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_wc__read_conflict_descriptions2_t(const apr_array_header_t **conflicts,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ return svn_wc__read_conflicts(conflicts, NULL, wc_ctx->db, local_abspath,
+ FALSE, FALSE, result_pool, scratch_pool);
+}
+
/*** Resolving a conflict automatically ***/
@@ -2477,15 +2489,12 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
if (!merged_value)
{
- svn_stream_t *stream;
- svn_string_t *merged_propval;
+ svn_stringbuf_t *merged_propval;
- SVN_ERR(svn_stream_open_readonly(&stream, merged_file,
- scratch_pool, scratch_pool));
- SVN_ERR(svn_string_from_stream(&merged_propval, stream,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_stringbuf_from_file2(&merged_propval, merged_file,
+ scratch_pool));
- merged_value = merged_propval;
+ merged_value = svn_stringbuf__morph_into_string(merged_propval);
}
svn_hash_sets(resolve_from, conflicted_propname, merged_value);
}
@@ -2594,6 +2603,35 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
return SVN_NO_ERROR;
}
+/*
+ * Record a tree conflict resolution failure due to error condition ERR
+ * in the RESOLVE_LATER hash table. If the hash table is not available
+ * (meaning the caller does not wish to retry resolution later), or if
+ * the error condition does not indicate circumstances where another
+ * existing tree conflict is blocking the resolution attempt, then
+ * return the error ERR itself.
+ */
+static svn_error_t *
+handle_tree_conflict_resolution_failure(const char *local_abspath,
+ svn_error_t *err,
+ apr_hash_t *resolve_later)
+{
+ const char *dup_abspath;
+
+ if (!resolve_later
+ || (err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE
+ && err->apr_err != SVN_ERR_WC_FOUND_CONFLICT))
+ return svn_error_trace(err); /* Give up. Do not retry resolution later. */
+
+ svn_error_clear(err);
+ dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
+ local_abspath);
+
+ svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
+
+ return SVN_NO_ERROR; /* Caller may retry after resolving other conflicts. */
+}
+
/*
* Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
* CONFLICT_CHOICE.
@@ -2603,9 +2641,11 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
*
* It is not an error if there is no tree conflict.
*
- * If the conflict can't be resolved yet because another tree conflict is
- * blocking a storage location, store the tree conflict in the RESOLVE_LATER
- * hash.
+ * If the conflict can't be resolved yet (e.g. because another tree conflict
+ * is blocking a storage location), and RESOLVE_LATER is not NULL, store the
+ * tree conflict in RESOLVE_LATER and do not mark the conflict resolved.
+ * Else if RESOLVE_LATER is NULL, do not mark the conflict resolved and
+ * return the error which prevented the conflict from being marked resolved.
*/
static svn_error_t *
resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
@@ -2670,8 +2710,9 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
{
svn_skel_t *new_conflicts;
- /* Raise moved-away conflicts on any children moved out of
- * this directory, and leave this directory as-is.
+ /* Raise local moved-away vs. incoming edit conflicts on
+ * any children moved out of this directory, and leave
+ * this directory as-is.
*
* The newly conflicted moved-away children will be updated
* if they are resolved with 'mine_conflict' as well. */
@@ -2680,21 +2721,8 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
scratch_pool);
if (err)
- {
- const char *dup_abspath;
-
- if (!resolve_later
- || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
- return svn_error_trace(err);
-
- svn_error_clear(err);
- dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
- local_abspath);
-
- svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
-
- return SVN_NO_ERROR; /* Retry after other conflicts */
- }
+ SVN_ERR(handle_tree_conflict_resolution_failure(
+ local_abspath, err, resolve_later));
/* We might now have a moved-away on *this* path, let's
try to resolve that directly if that is the case */
@@ -2713,7 +2741,7 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
if (!new_conflicts || !tree_conflicted)
{
/* TC is marked resolved by calling
- svn_wc__db_resolve_delete_raise_moved_away */
+ svn_wc__db_op_raise_moved_away */
*did_resolve = TRUE;
return SVN_NO_ERROR;
}
@@ -2761,21 +2789,8 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
scratch_pool);
if (err)
- {
- const char *dup_abspath;
-
- if (!resolve_later
- || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
- return svn_error_trace(err);
-
- svn_error_clear(err);
- dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
- local_abspath);
-
- svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
-
- return SVN_NO_ERROR; /* Retry after other conflicts */
- }
+ SVN_ERR(handle_tree_conflict_resolution_failure(
+ local_abspath, err, resolve_later));
else
*did_resolve = TRUE;
}
@@ -2973,12 +2988,13 @@ conflict_status_walker(void *baton,
{
struct conflict_status_walker_baton *cswb = baton;
svn_wc__db_t *db = cswb->db;
-
+ svn_wc_notify_action_t notify_action = svn_wc_notify_resolved;
const apr_array_header_t *conflicts;
apr_pool_t *iterpool;
int i;
svn_boolean_t resolved = FALSE;
svn_skel_t *conflict;
+ const svn_wc_conflict_description2_t *cd;
if (!status->conflicted)
return SVN_NO_ERROR;
@@ -2993,7 +3009,6 @@ conflict_status_walker(void *baton,
for (i = 0; i < conflicts->nelts; i++)
{
- const svn_wc_conflict_description2_t *cd;
svn_boolean_t did_resolve;
svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
svn_wc_conflict_result_t *result = NULL;
@@ -3045,7 +3060,10 @@ conflict_status_walker(void *baton,
iterpool));
if (did_resolve)
- resolved = TRUE;
+ {
+ resolved = TRUE;
+ notify_action = svn_wc_notify_resolved_tree;
+ }
break;
case svn_wc_conflict_kind_text:
@@ -3069,6 +3087,8 @@ conflict_status_walker(void *baton,
SVN_ERR(svn_wc__wq_run(db, local_abspath,
cswb->cancel_func, cswb->cancel_baton,
iterpool));
+ if (resolved)
+ notify_action = svn_wc_notify_resolved_text;
break;
case svn_wc_conflict_kind_property:
@@ -3089,7 +3109,10 @@ conflict_status_walker(void *baton,
iterpool));
if (did_resolve)
- resolved = TRUE;
+ {
+ resolved = TRUE;
+ notify_action = svn_wc_notify_resolved_prop;
+ }
break;
default:
@@ -3100,12 +3123,33 @@ conflict_status_walker(void *baton,
/* Notify */
if (cswb->notify_func && resolved)
- cswb->notify_func(cswb->notify_baton,
- svn_wc_create_notify(local_abspath,
- svn_wc_notify_resolved,
- iterpool),
- iterpool);
+ {
+ svn_wc_notify_t *notify;
+
+ /* If our caller asked for all conflicts to be resolved,
+ * send a general 'resolved' notification. */
+ if (cswb->resolve_text && cswb->resolve_tree &&
+ (cswb->resolve_prop == NULL || cswb->resolve_prop[0] == '\0'))
+ notify_action = svn_wc_notify_resolved;
+
+ /* If we resolved a property conflict, but no specific property was
+ * requested by the caller, send a general 'resolved' notification. */
+ if (notify_action == svn_wc_notify_resolved_prop &&
+ (cswb->resolve_prop == NULL || cswb->resolve_prop[0] == '\0'))
+ notify_action = svn_wc_notify_resolved;
+
+ notify = svn_wc_create_notify(local_abspath, notify_action, iterpool);
+
+ /* Add the property name for property-specific notifications. */
+ if (notify_action == svn_wc_notify_resolved_prop)
+ {
+ notify->prop_name = cd->property_name;
+ SVN_ERR_ASSERT(strlen(notify->prop_name) > 0);
+ }
+
+ cswb->notify_func(cswb->notify_baton, notify, iterpool);
+ }
if (resolved)
cswb->resolved_one = TRUE;
@@ -3130,26 +3174,11 @@ svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
void *notify_baton,
apr_pool_t *scratch_pool)
{
- svn_node_kind_t kind;
- svn_boolean_t conflicted;
struct conflict_status_walker_baton cswb;
apr_pool_t *iterpool = NULL;
svn_error_t *err;
- /* ### Just a versioned check? */
- /* Conflicted is set to allow invoking on actual only nodes */
- SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
- NULL, NULL, NULL, NULL, NULL, NULL,
- wc_ctx->db, local_abspath,
- scratch_pool, scratch_pool));
-
- /* When the implementation still used the entry walker, depth
- unknown was translated to infinity. */
- if (kind != svn_node_dir)
- depth = svn_depth_empty;
- else if (depth == svn_depth_unknown)
+ if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
cswb.db = wc_ctx->db;
@@ -3321,8 +3350,591 @@ svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
result->choice = choice;
result->merged_file = apr_pstrdup(pool, merged_file);
result->save_merged = FALSE;
+ result->merged_value = NULL;
/* If we add more fields to svn_wc_conflict_result_t, add them here. */
return result;
}
+
+svn_error_t *
+svn_wc__conflict_text_mark_resolved(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_wc_conflict_choice_t choice,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_skel_t *work_items;
+ svn_skel_t *conflict;
+ svn_boolean_t did_resolve;
+
+ SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
+ wc_ctx->db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (!conflict)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(build_text_conflict_resolve_items(&work_items, &did_resolve,
+ wc_ctx->db, local_abspath,
+ conflict, choice,
+ NULL, FALSE, NULL,
+ cancel_func, cancel_baton,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath,
+ TRUE, FALSE, FALSE,
+ work_items, scratch_pool));
+
+ SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
+ if (did_resolve && notify_func)
+ notify_func(notify_baton,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_resolved_text,
+ scratch_pool),
+ scratch_pool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__conflict_prop_mark_resolved(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const char *propname,
+ svn_wc_conflict_choice_t choice,
+ const svn_string_t *merged_value,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t did_resolve;
+ svn_skel_t *conflicts;
+
+ SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
+ wc_ctx->db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (!conflicts)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(resolve_prop_conflict_on_node(&did_resolve, wc_ctx->db,
+ local_abspath, conflicts,
+ propname, choice, NULL, merged_value,
+ NULL, NULL, scratch_pool));
+
+ if (did_resolve && notify_func)
+ {
+ svn_wc_notify_t *notify;
+
+ /* Send a general notification if no specific property was requested. */
+ if (propname == NULL || propname[0] == '\0')
+ {
+ notify = svn_wc_create_notify(local_abspath,
+ svn_wc_notify_resolved,
+ scratch_pool);
+ }
+ else
+ {
+ notify = svn_wc_create_notify(local_abspath,
+ svn_wc_notify_resolved_prop,
+ scratch_pool);
+ notify->prop_name = propname;
+ }
+
+ notify_func(notify_baton, notify, scratch_pool);
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__conflict_tree_update_break_moved_away(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_reason_t reason;
+ svn_wc_conflict_action_t action;
+ svn_wc_operation_t operation;
+ svn_boolean_t tree_conflicted;
+ const char *src_op_root_abspath;
+ const apr_array_header_t *conflicts;
+ svn_skel_t *conflict_skel;
+
+ SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
+ wc_ctx->db, local_abspath,
+ FALSE, /* no tempfiles */
+ FALSE, /* only tree conflicts */
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
+ &tree_conflicted, wc_ctx->db,
+ local_abspath, conflict_skel,
+ scratch_pool, scratch_pool));
+ if (!tree_conflicted)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
+ &src_op_root_abspath,
+ wc_ctx->db, local_abspath,
+ conflict_skel,
+ scratch_pool, scratch_pool));
+
+ /* Make sure the expected conflict is recorded. */
+ if (operation != svn_wc_operation_update &&
+ operation != svn_wc_operation_switch)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict operation '%s' on '%s'"),
+ svn_token__to_word(operation_map, operation),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (reason != svn_wc_conflict_reason_deleted &&
+ reason != svn_wc_conflict_reason_replaced &&
+ reason != svn_wc_conflict_reason_moved_away)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict reason '%s' on '%s'"),
+ svn_token__to_word(reason_map, reason),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ /* Break moves for any children moved out of this directory,
+ * and leave this directory deleted. */
+ if (action != svn_wc_conflict_action_delete)
+ {
+ SVN_ERR(svn_wc__db_op_break_moved_away(
+ wc_ctx->db, local_abspath, src_op_root_abspath, TRUE,
+ notify_func, notify_baton, scratch_pool));
+ /* Conflict was marked resolved by db_op_break_moved_away() call .*/
+
+ if (notify_func)
+ notify_func(notify_baton,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_resolved_tree,
+ scratch_pool),
+ scratch_pool);
+ return SVN_NO_ERROR;
+ }
+ /* else # The move is/moves are already broken */
+
+ SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath,
+ FALSE, FALSE, TRUE,
+ NULL, scratch_pool));
+ SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
+ scratch_pool));
+
+ if (notify_func)
+ notify_func(notify_baton,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_resolved_tree,
+ scratch_pool),
+ scratch_pool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__conflict_tree_update_raise_moved_away(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_reason_t reason;
+ svn_wc_conflict_action_t action;
+ svn_wc_operation_t operation;
+ svn_boolean_t tree_conflicted;
+ const apr_array_header_t *conflicts;
+ svn_skel_t *conflict_skel;
+
+ SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
+ wc_ctx->db, local_abspath,
+ FALSE, /* no tempfiles */
+ FALSE, /* only tree conflicts */
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
+ &tree_conflicted, wc_ctx->db,
+ local_abspath, conflict_skel,
+ scratch_pool, scratch_pool));
+ if (!tree_conflicted)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL,
+ wc_ctx->db, local_abspath,
+ conflict_skel,
+ scratch_pool, scratch_pool));
+
+ /* Make sure the expected conflict is recorded. */
+ if (operation != svn_wc_operation_update &&
+ operation != svn_wc_operation_switch)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict operation '%s' on '%s'"),
+ svn_token__to_word(operation_map, operation),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (reason != svn_wc_conflict_reason_deleted &&
+ reason != svn_wc_conflict_reason_replaced)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict reason '%s' on '%s'"),
+ svn_token__to_word(reason_map, reason),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (action != svn_wc_conflict_action_edit)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict action '%s' on '%s'"),
+ svn_token__to_word(action_map, action),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ /* Raise local moved-away vs. incoming edit conflicts on any children
+ * moved out of this directory, and leave this directory as-is.
+ * The user may choose to update newly conflicted moved-away children
+ * when resolving them. If this function raises an error, the conflict
+ * cannot be resolved yet because other conflicts or obstructions
+ * prevent us from propagating the conflict to moved-away children. */
+ SVN_ERR(svn_wc__db_op_raise_moved_away(wc_ctx->db, local_abspath,
+ notify_func, notify_baton,
+ scratch_pool));
+
+ /* The conflict was marked resolved by svn_wc__db_op_raise_moved_away(). */
+ if (notify_func)
+ notify_func(notify_baton,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_resolved_tree,
+ scratch_pool),
+ scratch_pool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__conflict_tree_update_moved_away_node(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_reason_t reason;
+ svn_wc_conflict_action_t action;
+ svn_wc_operation_t operation;
+ svn_boolean_t tree_conflicted;
+ const char *src_op_root_abspath;
+ const apr_array_header_t *conflicts;
+ svn_skel_t *conflict_skel;
+
+ SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
+ wc_ctx->db, local_abspath,
+ FALSE, /* no tempfiles */
+ FALSE, /* only tree conflicts */
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
+ &tree_conflicted, wc_ctx->db,
+ local_abspath, conflict_skel,
+ scratch_pool, scratch_pool));
+ if (!tree_conflicted)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
+ &src_op_root_abspath,
+ wc_ctx->db, local_abspath,
+ conflict_skel,
+ scratch_pool, scratch_pool));
+
+ /* Make sure the expected conflict is recorded. */
+ if (operation != svn_wc_operation_update &&
+ operation != svn_wc_operation_switch)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict operation '%s' on '%s'"),
+ svn_token__to_word(operation_map, operation),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (reason != svn_wc_conflict_reason_moved_away)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict reason '%s' on '%s'"),
+ svn_token__to_word(reason_map, reason),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (action != svn_wc_conflict_action_edit)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict action '%s' on '%s'"),
+ svn_token__to_word(action_map, action),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ /* Update the moved-away conflict victim. */
+ SVN_ERR(svn_wc__db_update_moved_away_conflict_victim(wc_ctx->db,
+ local_abspath,
+ src_op_root_abspath,
+ operation,
+ action,
+ reason,
+ cancel_func,
+ cancel_baton,
+ notify_func,
+ notify_baton,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath,
+ FALSE, FALSE, TRUE,
+ NULL, scratch_pool));
+ SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
+ scratch_pool));
+
+ if (notify_func)
+ notify_func(notify_baton,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_resolved_tree,
+ scratch_pool),
+ scratch_pool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__conflict_tree_update_incoming_move(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const char *dest_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_reason_t local_change;
+ svn_wc_conflict_action_t incoming_change;
+ svn_wc_operation_t operation;
+ svn_boolean_t tree_conflicted;
+ const apr_array_header_t *conflicts;
+ svn_skel_t *conflict_skel;
+
+ SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
+ wc_ctx->db, local_abspath,
+ FALSE, /* no tempfiles */
+ FALSE, /* only tree conflicts */
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
+ &tree_conflicted, wc_ctx->db,
+ local_abspath, conflict_skel,
+ scratch_pool, scratch_pool));
+ if (!tree_conflicted)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, &incoming_change,
+ NULL, wc_ctx->db, local_abspath,
+ conflict_skel,
+ scratch_pool, scratch_pool));
+
+ /* Make sure the expected conflict is recorded. */
+ if (operation != svn_wc_operation_update &&
+ operation != svn_wc_operation_switch &&
+ operation != svn_wc_operation_merge)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict operation '%s' on '%s'"),
+ svn_token__to_word(operation_map, operation),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (local_change != svn_wc_conflict_reason_edited)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict reason '%s' on '%s'"),
+ svn_token__to_word(reason_map, local_change),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (incoming_change != svn_wc_conflict_action_delete)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict action '%s' on '%s'"),
+ svn_token__to_word(action_map, incoming_change),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__db_update_incoming_move(wc_ctx->db, local_abspath,
+ dest_abspath, operation,
+ incoming_change, local_change,
+ cancel_func, cancel_baton,
+ notify_func, notify_baton,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__conflict_tree_update_local_add(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_reason_t local_change;
+ svn_wc_conflict_action_t incoming_change;
+ svn_wc_operation_t operation;
+ svn_boolean_t tree_conflicted;
+ const apr_array_header_t *conflicts;
+ svn_skel_t *conflict_skel;
+
+ SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
+ wc_ctx->db, local_abspath,
+ FALSE, /* no tempfiles */
+ FALSE, /* only tree conflicts */
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
+ &tree_conflicted, wc_ctx->db,
+ local_abspath, conflict_skel,
+ scratch_pool, scratch_pool));
+ if (!tree_conflicted)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, &incoming_change,
+ NULL, wc_ctx->db, local_abspath,
+ conflict_skel,
+ scratch_pool, scratch_pool));
+
+ /* Make sure the expected conflict is recorded. */
+ if (operation != svn_wc_operation_update)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict operation '%s' on '%s'"),
+ svn_token__to_word(operation_map, operation),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (local_change != svn_wc_conflict_reason_added)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict reason '%s' on '%s'"),
+ svn_token__to_word(reason_map, local_change),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+ if (incoming_change != svn_wc_conflict_action_add)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Unexpected conflict action '%s' on '%s'"),
+ svn_token__to_word(action_map, incoming_change),
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__db_update_local_add(wc_ctx->db, local_abspath,
+ cancel_func, cancel_baton,
+ notify_func, notify_baton,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__guess_incoming_move_target_nodes(apr_array_header_t **possible_targets,
+ svn_wc_context_t *wc_ctx,
+ const char *victim_abspath,
+ svn_node_kind_t victim_node_kind,
+ const char *moved_to_repos_relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *candidates;
+ apr_pool_t *iterpool;
+ int i;
+ apr_size_t longest_ancestor_len = 0;
+
+ *possible_targets = apr_array_make(result_pool, 1, sizeof(const char *));
+ SVN_ERR(svn_wc__find_repos_node_in_wc(&candidates, wc_ctx->db, victim_abspath,
+ moved_to_repos_relpath,
+ scratch_pool, scratch_pool));
+
+ /* Find a "useful move target" node in our set of candidates.
+ * Since there is no way to be certain, filter out nodes which seem
+ * unlikely candidates, and return the first node which is "good enough".
+ * Nodes which are tree conflict victims don't count, and nodes which
+ * cannot be modified (e.g. replaced or deleted nodes) don't count.
+ * Nodes which are of a different node kind don't count either.
+ * Ignore switched nodes as well, since that is an unlikely case during
+ * update/swtich/merge conflict resolution. And externals shouldn't even
+ * be on our candidate list in the first place.
+ * If multiple candidates match these criteria, choose the one which
+ * shares the longest common ancestor with the victim. */
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < candidates->nelts; i++)
+ {
+ const char *local_abspath;
+ const char *ancestor_abspath;
+ apr_size_t ancestor_len;
+ svn_boolean_t tree_conflicted;
+ svn_wc__db_status_t status;
+ svn_boolean_t is_wcroot;
+ svn_boolean_t is_switched;
+ svn_node_kind_t node_kind;
+ const char *moved_to_abspath;
+ int insert_index;
+
+ svn_pool_clear(iterpool);
+
+ local_abspath = APR_ARRAY_IDX(candidates, i, const char *);
+
+ SVN_ERR(svn_wc__internal_conflicted_p(NULL, NULL, &tree_conflicted,
+ wc_ctx->db, local_abspath,
+ iterpool));
+ if (tree_conflicted)
+ continue;
+
+ SVN_ERR(svn_wc__db_read_info(&status, &node_kind,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ wc_ctx->db, local_abspath, iterpool,
+ iterpool));
+ if (status != svn_wc__db_status_normal &&
+ status != svn_wc__db_status_added)
+ continue;
+
+ if (node_kind != victim_node_kind)
+ continue;
+
+ SVN_ERR(svn_wc__db_is_switched(&is_wcroot, &is_switched, NULL,
+ wc_ctx->db, local_abspath, iterpool));
+ if (is_wcroot || is_switched)
+ continue;
+
+ /* This might be a move target. Fingers crossed ;-) */
+ moved_to_abspath = apr_pstrdup(result_pool, local_abspath);
+
+ /* Insert the move target into the list. Targets which are closer
+ * (path-wise) to the conflict victim are more likely to be a good
+ * match, so put them at the front of the list. */
+ ancestor_abspath = svn_dirent_get_longest_ancestor(local_abspath,
+ victim_abspath,
+ iterpool);
+ ancestor_len = strlen(ancestor_abspath);
+ if (ancestor_len >= longest_ancestor_len)
+ {
+ longest_ancestor_len = ancestor_len;
+ insert_index = 0; /* prepend */
+ }
+ else
+ {
+ insert_index = (*possible_targets)->nelts; /* append */
+ }
+ svn_sort__array_insert(*possible_targets, &moved_to_abspath,
+ insert_index);
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}