You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2017/02/18 10:38:20 UTC

svn commit: r1783500 - in /subversion/trunk/subversion: include/svn_client.h libsvn_client/conflicts.c svn/conflict-callbacks.c

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 "