Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id D2E4A200C1F for ; Sat, 18 Feb 2017 11:38:23 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id D16DD160B66; Sat, 18 Feb 2017 10:38:23 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id D095C160B63 for ; Sat, 18 Feb 2017 11:38:22 +0100 (CET) Received: (qmail 17697 invoked by uid 500); 18 Feb 2017 10:38:21 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 17687 invoked by uid 99); 18 Feb 2017 10:38:21 -0000 Received: from Unknown (HELO svn01-us-west.apache.org) (209.188.14.144) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 18 Feb 2017 10:38:21 +0000 Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 053613A090E for ; Sat, 18 Feb 2017 10:38:20 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1783500 - in /subversion/trunk/subversion: include/svn_client.h libsvn_client/conflicts.c svn/conflict-callbacks.c Date: Sat, 18 Feb 2017 10:38:20 -0000 To: commits@subversion.apache.org From: stsp@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20170218103821.053613A090E@svn01-us-west.apache.org> archived-at: Sat, 18 Feb 2017 10:38:24 -0000 Author: stsp Date: Sat Feb 18 10:38:20 2017 New Revision: 1783500 URL: http://svn.apache.org/viewvc?rev=1783500&view=rev Log: Allow the conflict resolver to recommend resolution options and implement support for this feature in 'svn resolve'. Clients can now avoid interactive prompting in cases where resolution succeeds with the recommended resolution option. Recommendations are hard-coded in libsvn_client. Since only one possible option should be recommended, and clients are free to ignore recommendations made by libsvn_client, I don't see a point in making this configurable. At present libsvn_client only recommends options for tree conflicts which involve unambiguous moves, but this is an implementation detail. In the future, we may want to add an override to 'svn resolve', such as a new '--accept ask' option, for people who enjoy the conflict prompt so much that they want to see it all the time. * subversion/include/svn_client.h (svn_client_conflict_get_recommended_option_id): Declare. (svn_client_conflict_tree_resolve): Add another error code to the docstring. Knowing these error code is now important to client implementors because a recommended option may fail to resolve a conflict, in which case clients should fall back to trying other options (and, usually, prompting). * subversion/libsvn_client/conflicts.c (svn_client_conflict_t): Add 'recommended_option_id' field. (init_wc_move_targets): Recommend options for resolving tree conflicts involving unambiguous moves. (svn_client_conflict_get_recommended_option_id): New. (svn_client_conflict_get): Initialize 'recommended_option_id' field. * subversion/svn/conflict-callbacks.c (client_option_t) Add 'is_recommended' flag. (find_recommended_option): New helper function. (find_option_by_builtin): Add a 'conflict' paramter. Set 'is_recommended' flag for recommended options. (build_text_conflict_options, build_prop_conflict_options, build_tree_conflict_options): Adjust callers of find_option_by_builtin(). (handle_tree_conflict): If an option is recommended, try it before falling back to the interactive conflict prompt. Modified: subversion/trunk/subversion/include/svn_client.h subversion/trunk/subversion/libsvn_client/conflicts.c subversion/trunk/subversion/svn/conflict-callbacks.c Modified: subversion/trunk/subversion/include/svn_client.h URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1783500&r1=1783499&r2=1783500&view=diff ============================================================================== --- subversion/trunk/subversion/include/svn_client.h (original) +++ subversion/trunk/subversion/include/svn_client.h Sat Feb 18 10:38:20 2017 @@ -4788,6 +4788,27 @@ svn_client_conflict_option_get_descripti apr_pool_t *result_pool); /** + * Return the ID of the recommended resolution option. If no specific option + * is recommended, return @c svn_client_conflict_option_unspecified; + * + * Client implementations which aim to avoid excessive interactive prompting + * may wish to try a recommended resolution option before falling back to + * asking the user which option to use. + * + * Conflict resolution with a recommended option is not guaranteed to succeed. + * Clients should check for errors when trying to resolve a conflict and fall + * back to other options and/or interactive prompting when the recommended + * option fails to resolve a conflict. + * + * If @a conflict is a tree conflict, svn_client_conflict_tree_get_details() + * should be called before this function to allow for useful recommendations. + * + * @since New in 1.10. + */ +svn_client_conflict_option_id_t +svn_client_conflict_get_recommended_option_id(svn_client_conflict_t *conflict); + +/** * Return the absolute path to the conflicted working copy node described * by @a conflict. * @@ -4908,7 +4929,8 @@ 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. + * instance @c SVN_ERR_WC_OBSTRUCTED_UPDATE, @c SVN_ERR_WC_FOUND_CONFLICT, + * or @c SVN_ERR_WC_CONFLICT_RESOLVER_FAILUE. * 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 Modified: subversion/trunk/subversion/libsvn_client/conflicts.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/conflicts.c?rev=1783500&r1=1783499&r2=1783500&view=diff ============================================================================== --- subversion/trunk/subversion/libsvn_client/conflicts.c (original) +++ subversion/trunk/subversion/libsvn_client/conflicts.c Sat Feb 18 10:38:20 2017 @@ -107,6 +107,9 @@ struct svn_client_conflict_t const svn_wc_conflict_description2_t *legacy_text_conflict; const char *legacy_prop_conflict_propname; const svn_wc_conflict_description2_t *legacy_tree_conflict; + + /* The recommended resolution option's ID. */ + svn_client_conflict_option_id_t recommended_option_id; }; /* Resolves conflict to OPTION and sets CONFLICT->RESOLUTION accordingly. @@ -4081,6 +4084,39 @@ init_wc_move_targets(struct conflict_tre get_moved_to_repos_relpath(details, scratch_pool); details->wc_move_target_idx = 0; + /* If only one move target exists recommend a resolution option. */ + if (apr_hash_count(details->wc_move_targets) == 1) + { + apr_array_header_t *wc_abspaths; + + wc_abspaths = svn_hash_gets(details->wc_move_targets, + details->move_target_repos_relpath); + if (wc_abspaths->nelts == 1) + { + svn_client_conflict_option_id_t recommended[] = + { + /* Only one of these will be present for any given conflict. */ + svn_client_conflict_option_incoming_move_file_text_merge, + svn_client_conflict_option_incoming_move_dir_merge, + svn_client_conflict_option_local_move_file_text_merge + }; + apr_array_header_t *options; + + SVN_ERR(svn_client_conflict_tree_get_resolution_options( + &options, conflict, ctx, scratch_pool, scratch_pool)); + for (i = 0; i < (sizeof(recommended) / sizeof(recommended[0])); i++) + { + svn_client_conflict_option_id_t option_id = recommended[i]; + + if (svn_client_conflict_option_find_by_id(options, option_id)) + { + conflict->recommended_option_id = option_id; + break; + } + } + } + } + return SVN_NO_ERROR; } @@ -9708,6 +9744,12 @@ svn_client_conflict_option_get_descripti return apr_pstrdup(result_pool, option->description); } +svn_client_conflict_option_id_t +svn_client_conflict_get_recommended_option_id(svn_client_conflict_t *conflict) +{ + return conflict->recommended_option_id; +} + svn_error_t * svn_client_conflict_text_resolve(svn_client_conflict_t *conflict, svn_client_conflict_option_t *option, @@ -10229,6 +10271,7 @@ svn_client_conflict_get(svn_client_confl (*conflict)->resolution_text = svn_client_conflict_option_unspecified; (*conflict)->resolution_tree = svn_client_conflict_option_unspecified; (*conflict)->resolved_props = apr_hash_make(result_pool); + (*conflict)->recommended_option_id = svn_client_conflict_option_unspecified; (*conflict)->pool = result_pool; /* Add all legacy conflict descriptors we can find. Eventually, this code Modified: subversion/trunk/subversion/svn/conflict-callbacks.c URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?rev=1783500&r1=1783499&r2=1783500&view=diff ============================================================================== --- subversion/trunk/subversion/svn/conflict-callbacks.c (original) +++ subversion/trunk/subversion/svn/conflict-callbacks.c Sat Feb 18 10:38:20 2017 @@ -387,6 +387,7 @@ typedef struct client_option_t svn_client_conflict_option_id_t choice; /* or ..._undefined if not from libsvn_client */ const char *accept_arg; /* --accept option argument (NOT localized) */ + svn_boolean_t is_recommended; /* if TRUE, try this option before prompting */ } client_option_t; /* Resolver options for conflict options offered by libsvn_client. */ @@ -533,11 +534,29 @@ find_option(const apr_array_header_t *op return NULL; } +/* Find the first recommended option in OPTIONS. */ +static const client_option_t * +find_recommended_option(const apr_array_header_t *options) +{ + int i; + + for (i = 0; i < options->nelts; i++) + { + const client_option_t *opt = APR_ARRAY_IDX(options, i, client_option_t *); + + /* Ignore code "" (blank lines) which is not a valid answer. */ + if (opt->code[0] && opt->is_recommended) + return opt; + } + return NULL; +} + /* Return a pointer to the client_option_t in OPTIONS matching the ID of * conflict option BUILTIN_OPTION. @a out will be set to NULL if the * option was not found. */ static svn_error_t * find_option_by_builtin(client_option_t **out, + svn_client_conflict_t *conflict, const resolver_option_t *options, svn_client_conflict_option_t *builtin_option, apr_pool_t *result_pool, @@ -545,8 +564,10 @@ find_option_by_builtin(client_option_t * { const resolver_option_t *opt; svn_client_conflict_option_id_t id; + svn_client_conflict_option_id_t recommended_id; id = svn_client_conflict_option_get_id(builtin_option); + recommended_id = svn_client_conflict_get_recommended_option_id(conflict); for (opt = options; opt->code; opt++) { @@ -564,6 +585,9 @@ find_option_by_builtin(client_option_t * builtin_option, result_pool); client_opt->accept_arg = opt->accept_arg; + client_opt->is_recommended = + (recommended_id != svn_client_conflict_option_unspecified && + id == recommended_id); *out = client_opt; @@ -753,7 +777,7 @@ build_text_conflict_options(apr_array_he svn_pool_clear(iterpool); builtin_option = APR_ARRAY_IDX(builtin_options, i, svn_client_conflict_option_t *); - SVN_ERR(find_option_by_builtin(&opt, + SVN_ERR(find_option_by_builtin(&opt, conflict, builtin_resolver_options, builtin_option, result_pool, @@ -1213,7 +1237,7 @@ build_prop_conflict_options(apr_array_he svn_pool_clear(iterpool); builtin_option = APR_ARRAY_IDX(builtin_options, i, svn_client_conflict_option_t *); - SVN_ERR(find_option_by_builtin(&opt, + SVN_ERR(find_option_by_builtin(&opt, conflict, builtin_resolver_options, builtin_option, result_pool, @@ -1472,7 +1496,7 @@ build_tree_conflict_options( svn_pool_clear(iterpool); builtin_option = APR_ARRAY_IDX(builtin_options, i, svn_client_conflict_option_t *); - SVN_ERR(find_option_by_builtin(&opt, + SVN_ERR(find_option_by_builtin(&opt, conflict, builtin_resolver_options, builtin_option, result_pool, @@ -1668,6 +1692,7 @@ handle_tree_conflict(svn_boolean_t *reso apr_array_header_t *possible_moved_to_repos_relpaths; apr_array_header_t *possible_moved_to_abspaths; svn_boolean_t all_options_are_dumb; + const struct client_option_t *recommended_option; option_id = svn_client_conflict_option_unspecified; local_abspath = svn_client_conflict_get_local_abspath(conflict); @@ -1695,6 +1720,37 @@ handle_tree_conflict(svn_boolean_t *reso conflict, ctx, scratch_pool, scratch_pool)); + /* Try a recommended resolution option before prompting. */ + recommended_option = find_recommended_option(tree_conflict_options); + if (recommended_option) + { + svn_error_t *err; + apr_status_t root_cause; + + SVN_ERR(svn_cmdline_printf(scratch_pool, + _("Applying recommended resolution '%s':\n"), + recommended_option->label)); + + err = mark_conflict_resolved(conflict, recommended_option->choice, + FALSE, NULL, TRUE, + path_prefix, conflict_stats, + ctx, scratch_pool); + if (!err) + { + *resolved = TRUE; + return SVN_NO_ERROR; + } + + root_cause = svn_error_root_cause(err)->apr_err; + if (root_cause != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE && + root_cause != SVN_ERR_WC_OBSTRUCTED_UPDATE && + root_cause != SVN_ERR_WC_FOUND_CONFLICT) + return svn_error_trace(err); + + /* Fall back to interactive prompting. */ + svn_error_clear(err); + } + if (all_options_are_dumb) SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, _("\nSubversion is not smart enough to resolve "