diff options
| author | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 | 
|---|---|---|
| committer | Peter Wemm <peter@FreeBSD.org> | 2018-05-08 03:44:38 +0000 | 
| commit | 3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch) | |
| tree | 7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion/libsvn_wc/conflicts.c | |
| parent | a55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff) | |
Notes
Diffstat (limited to 'subversion/libsvn_wc/conflicts.c')
| -rw-r--r-- | subversion/libsvn_wc/conflicts.c | 752 | 
1 files changed, 682 insertions, 70 deletions
| diff --git a/subversion/libsvn_wc/conflicts.c b/subversion/libsvn_wc/conflicts.c index f04c6de59481..606710c69f00 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; +} | 
