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 2016/02/23 18:21:38 UTC

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

Author: stsp
Date: Tue Feb 23 17:21:37 2016
New Revision: 1731907

URL: http://svn.apache.org/viewvc?rev=1731907&view=rev
Log:
Introduce a mechanism by which tree conflicts can describe themselves in a
conflict-specific manner. Also add a way to fetch additional information
from the repository to enhance this description. The latter feature is
exposed as a new public API function for use by conflict resolvers.

In 'svn', this feature is only available at the interactive conflict prompt.
As a first simple test case allow for finding the revision in which an
incoming deletion happened in the repository.

* subversion/include/svn_client.h
  (svn_client_conflict_tree_get_description,
   svn_client_conflict_tree_get_resolution_options): Document interaction
    with svn_client_conflict_tree_get_details().
  (svn_client_conflict_tree_get_details): Declare. This function can be used
   by clients to get more details about a tree conflict from the repository.

* subversion/libsvn_client/conflicts.c
  (tree_conflict_get_description_func_t,
   tree_conflict_get_details_func_t): New function pointer types.
  (svn_client_conflict_t): Add new fields needed for conflict-specific
   description and detail methods.
  (conflict_type_specific_setup): New helper function. This is where we
   can plug type-specific elements into a tree conflict object.
  (conflict_get_internal): Initialize new fields.
  (conflict_tree_get_description_incoming_delete,
   conflict_tree_get_details_incoming_delete): New helper functions. These
    are specific to tree conflicts which involve an incoming deletion.
  (conflict_tree_get_description_generic): New. The default function for
   getting tree conflict descriptions. It does what ...
  (svn_client_conflict_tree_get_description): ... this function used to do.
   Which now calls a conflict-specific handler to get a description.
  (svn_client_conflict_tree_get_details): Implement.

* subversion/svn/conflict-callbacks.c
  (extra_resolver_options_tree): Add "g" option ("get more details").
  (handle_tree_conflict): Handle the new "g" option.

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=1731907&r1=1731906&r2=1731907&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Tue Feb 23 17:21:37 2016
@@ -4507,6 +4507,11 @@ svn_client_conflict_prop_get_description
  * Additionally, the description may be localized to the language used
  * by the current locale.
  *
+ * By default, the description is based only on information available in
+ * the working copy. If svn_client_conflict_tree_get_details() was already
+ * called for @a conflict, the description might also contain useful
+ * information obtained from the repository.
+ *
  * @since New in 1.10.
  */
 svn_error_t *
@@ -4543,6 +4548,10 @@ svn_client_conflict_prop_get_resolution_
  * Set @a *options to an array of pointers to svn_client_conflict_option_t
  * objects applicable to the tree conflict described by @a conflict.
  *
+ * By default, the list of options is based only on information available in
+ * the working copy. If svn_client_conflict_tree_get_details() was already
+ * called for @a conflict, a more useful list of options might be returned.
+ *
  * @since New in 1.10.
  */
 svn_error_t *
@@ -4552,6 +4561,23 @@ svn_client_conflict_tree_get_resolution_
                                                 apr_pool_t *scratch_pool);
 
 /**
+ * Find more information about the tree conflict represented by @a conflict.
+ *
+ * A call to svn_client_conflict_tree_get_description() may yield much better
+ * results after this function has been called.
+ *
+ * A call to svn_client_conflict_tree_get_resolution_options() may provide
+ * more useful resolution options if this function has been called.
+ *
+ * This function may contact the repository.
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_tree_get_details(svn_client_conflict_t *conflict,
+                                     apr_pool_t *scratch_pool);
+
+/**
  * Return an ID for @a option. This ID can be used by callers to associate
  * arbitrary data with a particular conflict resolution option.
  *

Modified: subversion/trunk/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/conflicts.c?rev=1731907&r1=1731906&r2=1731907&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_client/conflicts.c Tue Feb 23 17:21:37 2016
@@ -48,6 +48,19 @@
 
 /*** Dealing with conflicts. ***/
 
+/* Describe a tree conflict. */
+typedef svn_error_t *(*tree_conflict_get_description_func_t)(
+  const char **description,
+  svn_client_conflict_t *conflict,
+  apr_pool_t *result_pool,
+  apr_pool_t *scratch_pool);
+
+/* Get more information about a tree conflict.
+ * This function may contact the repository. */
+typedef svn_error_t *(*tree_conflict_get_details_func_t)(
+  svn_client_conflict_t *conflict,
+  apr_pool_t *scratch_pool);
+
 struct svn_client_conflict_t
 {
   const char *local_abspath;
@@ -65,6 +78,20 @@ struct svn_client_conflict_t
    * the property conflicts. */
   apr_hash_t *resolved_props;
 
+  /* Ask a tree conflict to describe itself. */
+  tree_conflict_get_description_func_t tree_conflict_get_description_func;
+
+  /* Ask a tree conflict to find out more information about itself
+   * by contacting the repository. */
+  tree_conflict_get_details_func_t tree_conflict_get_details_func;
+
+  /* Any additional information found can be stored here and may be used
+   * when describing a tree conflict. */
+  void *tree_conflict_details;
+
+  /* The pool this conflict was allocated from. */
+  apr_pool_t *pool;
+
   /* For backwards compat. */
   const svn_wc_conflict_description2_t *legacy_text_conflict;
   const char *legacy_prop_conflict_propname;
@@ -176,6 +203,58 @@ add_legacy_desc_to_conflict(const svn_wc
     }
 }
 
+/* ### forward declarations */
+static svn_error_t *
+conflict_tree_get_description_generic(const char **description,
+                                      svn_client_conflict_t *conflict,
+                                      apr_pool_t *result_pool,
+                                      apr_pool_t *scratch_pool);
+static svn_error_t *
+conflict_tree_get_description_incoming_delete(const char **description,
+                                              svn_client_conflict_t *conflict,
+                                              apr_pool_t *result_pool,
+                                              apr_pool_t *scratch_pool);
+static svn_error_t *
+conflict_tree_get_details_incoming_delete(svn_client_conflict_t *conflict,
+                                          apr_pool_t *scratch_pool);
+
+/* Set up type-specific data for a new conflict object. */
+static svn_error_t *
+conflict_type_specific_setup(svn_client_conflict_t *conflict,
+                             apr_pool_t *scratch_pool)
+{
+  svn_boolean_t tree_conflicted;
+  svn_wc_operation_t operation;
+  svn_wc_conflict_reason_t local_change;
+  svn_wc_conflict_action_t incoming_change;
+
+  /* For now, we only deal with tree conflicts here. */
+  SVN_ERR(svn_client_conflict_get_conflicted(NULL, NULL, &tree_conflicted,
+                                             conflict, scratch_pool,
+                                             scratch_pool));
+  if (!tree_conflicted)
+    return SVN_NO_ERROR;
+
+  /* Set a default description function. */
+  conflict->tree_conflict_get_description_func =
+    conflict_tree_get_description_generic;
+
+  operation = svn_client_conflict_get_operation(conflict);
+  local_change = svn_client_conflict_get_local_change(conflict);
+  incoming_change = svn_client_conflict_get_incoming_change(conflict);
+
+  /* Set type-specific description and details functions if available. */
+  if (incoming_change == svn_wc_conflict_action_delete)
+    {
+      conflict->tree_conflict_get_description_func =
+        conflict_tree_get_description_incoming_delete;
+      conflict->tree_conflict_get_details_func =
+        conflict_tree_get_details_incoming_delete;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Set up a conflict object. If legacy conflict descriptor DESC is not NULL,
  * set up the conflict object for backwards compatibility. */
 static svn_error_t *
@@ -208,6 +287,7 @@ conflict_get_internal(svn_client_conflic
   (*conflict)->resolution_tree = svn_client_conflict_option_unspecified;
   (*conflict)->resolved_props = apr_hash_make(result_pool);
   (*conflict)->ctx = ctx;
+  (*conflict)->pool = result_pool;
 
   /* Add all legacy conflict descriptors we can find. Eventually, this code
    * path should stop relying on svn_wc_conflict_description2_t entirely. */
@@ -220,6 +300,8 @@ conflict_get_internal(svn_client_conflic
       add_legacy_desc_to_conflict(desc, *conflict, result_pool);
     }
 
+  SVN_ERR(conflict_type_specific_setup(*conflict, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -491,11 +573,12 @@ svn_client_conflict_prop_get_description
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_client_conflict_tree_get_description(const char **description,
-                                         svn_client_conflict_t *conflict,
-                                         apr_pool_t *result_pool,
-                                         apr_pool_t *scratch_pool)
+/* Implements tree_conflict_get_description_func_t. */
+static svn_error_t *
+conflict_tree_get_description_generic(const char **description,
+                                      svn_client_conflict_t *conflict,
+                                      apr_pool_t *result_pool,
+                                      apr_pool_t *scratch_pool)
 {
   const char *action, *reason, *operation;
   svn_node_kind_t incoming_kind;
@@ -560,6 +643,117 @@ svn_client_conflict_tree_get_description
   return SVN_NO_ERROR;
 }
 
+/* Implements tree_conflict_get_description_func_t. */
+static svn_error_t *
+conflict_tree_get_description_incoming_delete(const char **description,
+                                              svn_client_conflict_t *conflict,
+                                              apr_pool_t *result_pool,
+                                              apr_pool_t *scratch_pool)
+{
+  const char *action, *reason, *operation;
+  svn_revnum_t deleted_rev;
+  svn_node_kind_t incoming_node_kind;
+  svn_node_kind_t victim_node_kind;
+  svn_wc_conflict_action_t incoming_change;
+  svn_wc_conflict_reason_t local_change;
+  svn_wc_operation_t conflict_operation;
+
+  if (conflict->tree_conflict_details == NULL)
+    return svn_error_trace(conflict_tree_get_description_generic(description,
+                                                                 conflict,
+                                                                 result_pool,
+                                                                 scratch_pool));
+
+  incoming_change = svn_client_conflict_get_incoming_change(conflict);
+  local_change = svn_client_conflict_get_local_change(conflict);
+  conflict_operation = svn_client_conflict_get_operation(conflict);
+  victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
+  reason = local_reason_str(victim_node_kind, local_change, conflict_operation);
+  if (reason == NULL)
+    return svn_error_trace(conflict_tree_get_description_generic(description,
+                                                                 conflict,
+                                                                 result_pool,
+                                                                 scratch_pool));
+
+  deleted_rev = *((svn_revnum_t *)conflict->tree_conflict_details);
+
+  /* Delete is acting on 'src_left' version of the node. */
+  SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+            NULL, NULL, &incoming_node_kind, conflict, scratch_pool,
+            scratch_pool));
+  if (incoming_node_kind == svn_node_dir)
+    action = apr_psprintf(result_pool,
+                          _("incoming dir deleted or moved in r%lu"),
+                          deleted_rev);
+  else if (incoming_node_kind == svn_node_file ||
+           incoming_node_kind == svn_node_symlink)
+    action = apr_psprintf(result_pool,
+                          _("incoming file deleted or moved in r%lu"),
+                          deleted_rev);
+  else
+    action = apr_psprintf(result_pool,
+                          _("incoming item deleted or moved in r%lu"),
+                          deleted_rev);
+
+  operation = operation_str(conflict_operation);
+  SVN_ERR_ASSERT(operation);
+
+  *description = apr_psprintf(result_pool, _("%s, %s %s"),
+                              reason, action, operation);
+  return SVN_NO_ERROR;
+}
+
+/* Implements tree_conflict_get_details_func_t.
+ * Find the revision in which the victim was deleted in the repository. */
+static svn_error_t *
+conflict_tree_get_details_incoming_delete(svn_client_conflict_t *conflict,
+                                          apr_pool_t *scratch_pool)
+{
+  svn_revnum_t deleted_rev;
+  const char *repos_relpath;
+  const char *repos_root_url;
+  svn_revnum_t peg_rev;
+  svn_revnum_t end_rev;
+  const char *url;
+  const char *corrected_url;
+  svn_ra_session_t *ra_session;
+
+  SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+            &repos_relpath, &peg_rev, NULL, conflict, scratch_pool,
+            scratch_pool));
+  SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+            NULL, &end_rev, NULL, conflict, scratch_pool,
+            scratch_pool));
+  SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL, conflict,
+                                             scratch_pool, scratch_pool));
+  url = svn_path_url_add_component2(repos_root_url, repos_relpath,
+                                    scratch_pool);
+  SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
+                                               url, NULL, NULL,
+                                               FALSE /* write_dav_props */,
+                                               FALSE /* read_dav_props */,
+                                               conflict->ctx,
+                                               scratch_pool, scratch_pool));
+  SVN_ERR(svn_ra_get_deleted_rev(ra_session, "", peg_rev, end_rev,
+                                 &deleted_rev, scratch_pool));
+
+  conflict->tree_conflict_details = apr_pcalloc(conflict->pool,
+                                                sizeof(deleted_rev));
+  *((svn_revnum_t *)conflict->tree_conflict_details) = deleted_rev;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_conflict_tree_get_description(const char **description,
+                                         svn_client_conflict_t *conflict,
+                                         apr_pool_t *result_pool,
+                                         apr_pool_t *scratch_pool)
+{
+  return svn_error_trace(conflict->tree_conflict_get_description_func(
+                           description, conflict, result_pool, scratch_pool));
+}
+
 void
 svn_client_conflict_option_set_merged_propval(
   svn_client_conflict_option_t *option,
@@ -1184,6 +1378,18 @@ svn_client_conflict_tree_get_resolution_
 
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_client_conflict_tree_get_details(svn_client_conflict_t *conflict,
+                                     apr_pool_t *scratch_pool)
+{
+  SVN_ERR(assert_tree_conflict(conflict, scratch_pool));
+
+  if (conflict->tree_conflict_get_details_func)
+    SVN_ERR(conflict->tree_conflict_get_details_func(conflict, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
 
 svn_client_conflict_option_id_t
 svn_client_conflict_option_get_id(svn_client_conflict_option_t *option)

Modified: subversion/trunk/subversion/svn/conflict-callbacks.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?rev=1731907&r1=1731906&r2=1731907&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/conflict-callbacks.c (original)
+++ subversion/trunk/subversion/svn/conflict-callbacks.c Tue Feb 23 17:21:37 2016
@@ -476,6 +476,8 @@ static const resolver_option_t extra_res
 {
   /* Translators: keep long_desc below 70 characters (wrap with a left
      margin of 9 spaces if needed) */
+  { "g",  N_("get more details"), N_("get more information from repository"),
+                                   svn_client_conflict_option_undefined },
   { "h",  N_("help"),             N_("show this help (also '?')"),
                                    svn_client_conflict_option_undefined },
   { NULL }
@@ -1488,6 +1490,22 @@ handle_tree_conflict(svn_boolean_t *reso
           *quit = TRUE;
           break;
         }
+      else if (strcmp(opt->code, "g") == 0)
+        {
+          /* User wants to get more information from the repository. */
+          SVN_ERR(svn_client_conflict_tree_get_details(conflict, scratch_pool));
+
+          /* Description might have changed. Show it again. */
+          SVN_ERR(svn_client_conflict_tree_get_description(
+                   &description, conflict, scratch_pool, scratch_pool));
+          SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, _("   > %s\n"),
+                                      description));
+
+          /* Options might have changed. Rebuild the list. */
+          SVN_ERR(build_tree_conflict_options(&tree_conflict_options, conflict,
+                                              scratch_pool, scratch_pool));
+          continue;
+        }
       else if (opt->choice != svn_client_conflict_option_undefined)
         {
           option_id = opt->choice;