Author: stsp Date: Tue Feb 16 17:05:25 2016 New Revision: 1730716 URL: http://svn.apache.org/viewvc?rev=1730716&view=rev Log: Introduce svn_wc__conflict_tree_update_raise_moved_away(), a private libsvn_wc API function for use by libsvn_client's conflict resolver.
This API may raise an error if other tree conflicts need to be resolved before the tree conflict specified by the caller. Make the new conflict resolver handle this case. (This behaviour is now exposed to the public API rather than hidden in libsvn_wc as was the case with the legacy conflict resolver.) * subversion/include/private/svn_wc_private.h (svn_wc__conflict_tree_update_raise_moved_away): Declare. * subversion/include/svn_client.h (svn_client_conflict_tree_resolve): Document new error behavior in case a conflict cannot be resolved yet. * subversion/libsvn_client/resolved.c (conflict_option_resolve_func_t): Document new error behavior in case a conflict cannot be resolved yet. (resolve_tree_conflict): Use svn_wc__conflict_tree_update_raise_moved_away() if applicable. * subversion/libsvn_wc/conflicts.c (svn_wc__conflict_tree_update_raise_moved_away): Implement. * subversion/svn/resolve-cmd.c (handle_tree_conflict_resolution_failure): New helper function. Copied from legacy libsvn_wc conflict resolver code. (conflict_status_walker): Handle tree conflicts which cannot be resolved right away by retrying them later after resolving other conflicts first. Modified: subversion/trunk/subversion/include/private/svn_wc_private.h subversion/trunk/subversion/include/svn_client.h subversion/trunk/subversion/libsvn_client/resolved.c subversion/trunk/subversion/libsvn_wc/conflicts.c subversion/trunk/subversion/svn/resolve-cmd.c Modified: subversion/trunk/subversion/include/private/svn_wc_private.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_wc_private.h?rev=1730716&r1=1730715&r2=1730716&view=diff ============================================================================== --- subversion/trunk/subversion/include/private/svn_wc_private.h (original) +++ subversion/trunk/subversion/include/private/svn_wc_private.h Tue Feb 16 17:05:25 2016 @@ -1813,6 +1813,48 @@ svn_wc__conflict_tree_update_break_moved void *notify_baton, apr_pool_t *scratch_pool); + +/* Resolve a tree conflict where the victim at LOCAL_ABSPATH is a directory + * which was locally deleted or replaced, and which received an edit (some + * change inside the directory, or a change to the direcotory's properties) + * during an update or switch operation. + * + * The conflict is resolved by keeping the victim deleted, and propagating + * its tree conflict to any children which were moved out of the directory + * before the update operation. + * As a result, any such files or directories become victims of the tree + * conflict as well and must be resolved independently. + * Additionally, LOCAL_ABSPATH itself may become the victim of a different + * tree conflict as a result of resolving the existing tree conflict. + * + * The tree conflict at LOCAL_ABSPATH must have the following properties or + * SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE will be returned: + * + * operation: svn_wc_operation_update or svn_wc_operation_switch + * local change: svn_wc_conflict_reason_deleted or + * svn_wc_conflict_reason_replaced + * incoming change: svn_wc_conflict_action_edit + * + * If this conflict cannot be resolved because the conflict cannot be + * propagated to moved-away children, this function returns + * SVN_ERR_WC_OBSTRUCTED_UPDATE or SVN_ERR_WC_FOUND_CONFLICT. + * The caller should continue by resolving other conflicts and attempt to + * resolve this conflict again later. + * + * The working copy must already be locked for resolving, e.g. by calling + * svn_wc__acquire_write_lock_for_resolve() first. + * + * @since New in 1.10. + */ +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); + /** * Move @a src_abspath to @a dst_abspath, by scheduling @a dst_abspath * for addition to the repository, remembering the history. Mark @a src_abspath Modified: subversion/trunk/subversion/include/svn_client.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1730716&r1=1730715&r2=1730716&view=diff ============================================================================== --- subversion/trunk/subversion/include/svn_client.h (original) +++ subversion/trunk/subversion/include/svn_client.h Tue Feb 16 17:05:25 2016 @@ -4717,6 +4717,13 @@ svn_client_conflict_tree_get_victim_node /** * Resolve a tree @a conflict using resolution option @a option. * + * May raise an error in case the tree conflict cannot be resolved yet, for + * instance @c SVN_ERR_WC_OBSTRUCTED_UPDATE or @c SVN_ERR_WC_FOUND_CONFLICT. + * This may happen when other tree conflicts, or unversioned obstructions, + * block the resolution of this tree conflict. In such a case the other + * conflicts should be resolved first and resolution of this conflict should + * be attempted again later. + * * @since New in 1.10. */ svn_error_t * Modified: subversion/trunk/subversion/libsvn_client/resolved.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/resolved.c?rev=1730716&r1=1730715&r2=1730716&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_client/resolved.c (original) +++ subversion/trunk/subversion/libsvn_client/resolved.c Tue Feb 16 17:05:25 2016 @@ -175,7 +175,11 @@ struct svn_client_conflict_t const svn_wc_conflict_description2_t *legacy_tree_conflict; }; -/* Resolves conflict to OPTION and sets CONFLICT->RESOLUTION accordingly. */ +/* Resolves conflict to OPTION and sets CONFLICT->RESOLUTION accordingly. + * + * May raise an error in case the conflict could not be resolved. A common + * case would be a tree conflict the resolution of which depends on other + * tree conflicts to be resolved first. */ typedef svn_error_t *(*conflict_option_resolve_func_t)( svn_client_conflict_option_t *option, svn_client_conflict_t *conflict, @@ -793,6 +797,7 @@ resolve_tree_conflict(svn_client_conflic const char *local_abspath; const char *lock_abspath; svn_wc_conflict_reason_t local_change; + svn_wc_conflict_action_t incoming_change; svn_client_ctx_t *ctx = conflict->ctx; svn_wc_operation_t operation; svn_error_t *err; @@ -806,19 +811,30 @@ resolve_tree_conflict(svn_client_conflic local_abspath, scratch_pool, scratch_pool)); - if (option_id == svn_client_conflict_option_merged_text && + if ((option_id == svn_client_conflict_option_merged_text || + (option_id == svn_client_conflict_option_update_any_moved_away_children + && incoming_change == svn_wc_conflict_action_edit)) && (operation == svn_wc_operation_update || operation == svn_wc_operation_switch) && (local_change == svn_wc_conflict_reason_deleted || local_change == svn_wc_conflict_reason_replaced)) { - err = svn_wc__conflict_tree_update_break_moved_away(ctx->wc_ctx, - local_abspath, - ctx->cancel_func, - ctx->cancel_baton, - ctx->notify_func2, - ctx->notify_baton2, - scratch_pool); + if (option_id == svn_client_conflict_option_merged_text) + err = svn_wc__conflict_tree_update_break_moved_away(ctx->wc_ctx, + local_abspath, + ctx->cancel_func, + ctx->cancel_baton, + ctx->notify_func2, + ctx->notify_baton2, + scratch_pool); + else + err = svn_wc__conflict_tree_update_raise_moved_away(ctx->wc_ctx, + local_abspath, + ctx->cancel_func, + ctx->cancel_baton, + ctx->notify_func2, + ctx->notify_baton2, + scratch_pool); } else { Modified: subversion/trunk/subversion/libsvn_wc/conflicts.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/conflicts.c?rev=1730716&r1=1730715&r2=1730716&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_wc/conflicts.c (original) +++ subversion/trunk/subversion/libsvn_wc/conflicts.c Tue Feb 16 17:05:25 2016 @@ -3472,3 +3472,76 @@ svn_wc__conflict_tree_update_break_moved 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 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) + 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)); + + /* 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, + scratch_pool), + scratch_pool); + + return SVN_NO_ERROR; +} + Modified: subversion/trunk/subversion/svn/resolve-cmd.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/resolve-cmd.c?rev=1730716&r1=1730715&r2=1730716&view=diff ============================================================================== --- subversion/trunk/subversion/svn/resolve-cmd.c (original) +++ subversion/trunk/subversion/svn/resolve-cmd.c Tue Feb 16 17:05:25 2016 @@ -88,6 +88,35 @@ tree_conflict_collector(void *baton, } } +/* + * 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. */ +} + /* Implements svn_wc_status4_t to walk all conflicts to resolve. */ static svn_error_t * @@ -100,6 +129,7 @@ conflict_status_walker(void *baton, apr_pool_t *iterpool; svn_boolean_t resolved = FALSE; svn_client_conflict_t *conflict; + svn_error_t *err; if (!status->conflicted) return SVN_NO_ERROR; @@ -108,14 +138,24 @@ conflict_status_walker(void *baton, SVN_ERR(svn_client_conflict_get(&conflict, local_abspath, cswb->ctx, iterpool, iterpool)); - SVN_ERR(svn_cl__resolve_conflict(&resolved, cswb->accept_which, - cswb->quit, cswb->external_failed, - cswb->printed_summary, - conflict, cswb->editor_cmd, - cswb->config, cswb->path_prefix, - cswb->pb, cswb->conflict_stats, - cswb->option_id, cswb->ctx, - scratch_pool)); + err = svn_cl__resolve_conflict(&resolved, cswb->accept_which, + cswb->quit, cswb->external_failed, + cswb->printed_summary, + conflict, cswb->editor_cmd, + cswb->config, cswb->path_prefix, + cswb->pb, cswb->conflict_stats, + cswb->option_id, cswb->ctx, + scratch_pool); + if (err) + { + if (svn_client_conflict_get_kind(conflict) == svn_wc_conflict_kind_tree) + SVN_ERR(handle_tree_conflict_resolution_failure(local_abspath, err, + cswb->resolve_later)); + + else + return svn_error_trace(err); + } + if (resolved) cswb->resolved_one = TRUE;