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;