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/04/19 13:08:37 UTC

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

Author: stsp
Date: Tue Apr 19 11:08:37 2016
New Revision: 1739897

URL: http://svn.apache.org/viewvc?rev=1739897&view=rev
Log:
Add another resolution option for file/file tree conflict "incoming add vs.
local obstruction upon merge". This new option replaces the local file
with the incoming fileand then merges content.

Again, the implementation is rather crude since modifications made to the
working copy are not atomic. This will be addressed later.

* subversion/include/svn_client.h
  (svn_client_conflict_option_merge_incoming_added_file_replace_and_merge):
   New conflict option ID.

* subversion/libsvn_client/conflicts.c
  (resolve_merge_incoming_added_file_replace_and_merge,
   configure_option_merge_incoming_added_file_replace_and_merge): New.
  (svn_client_conflict_tree_get_resolution_options): Handle the new option.

* subversion/svn/conflict-callbacks.c
  (builtin_resolver_options): Bind a menu key code to the new option and
   provide a short description.

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=1739897&r1=1739896&r2=1739897&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Tue Apr 19 11:08:37 2016
@@ -4408,6 +4408,7 @@ typedef enum svn_client_conflict_option_
 
   /* Options for incoming file add vs local file 'obstruction' on merge. */
   svn_client_conflict_option_merge_incoming_added_file_text_merge,
+  svn_client_conflict_option_merge_incoming_added_file_replace_and_merge,
 
 } svn_client_conflict_option_id_t;
 

Modified: subversion/trunk/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/conflicts.c?rev=1739897&r1=1739896&r2=1739897&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_client/conflicts.c Tue Apr 19 11:08:37 2016
@@ -3724,6 +3724,195 @@ resolve_merge_incoming_added_file_text_m
   return SVN_NO_ERROR;
 }
 
+/* Implements conflict_option_resolve_func_t. */
+static svn_error_t *
+resolve_merge_incoming_added_file_replace_and_merge(
+  svn_client_conflict_option_t *option,
+  svn_client_conflict_t *conflict,
+  apr_pool_t *scratch_pool)
+{
+  svn_ra_session_t *ra_session;
+  const char *url;
+  const char *corrected_url;
+  const char *repos_root_url;
+  const char *repos_uuid;
+  const char *incoming_new_repos_relpath;
+  svn_revnum_t incoming_new_pegrev;
+  apr_file_t *incoming_new_file;
+  svn_stream_t *incoming_new_stream;
+  apr_hash_t *incoming_new_props;
+  const char *local_abspath;
+  const char *lock_abspath;
+  svn_client_ctx_t *ctx = conflict->ctx;
+  svn_wc_merge_outcome_t merge_content_outcome;
+  svn_wc_notify_state_t merge_props_outcome;
+  const char *wc_tmpdir;
+  apr_file_t *working_file_tmp;
+  svn_stream_t *working_file_tmp_stream;
+  const char *working_file_tmp_abspath;
+  svn_stream_t *working_file_stream;
+  apr_hash_t *working_props;
+  apr_file_t *empty_file;
+  const char *empty_file_abspath;
+  apr_array_header_t *propdiffs;
+  svn_error_t *err;
+
+  local_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+  /* Set up tempory storage for the working version of file. */
+  SVN_ERR(svn_wc__get_tmpdir(&wc_tmpdir, ctx->wc_ctx, local_abspath,
+                             scratch_pool, scratch_pool));
+  SVN_ERR(svn_io_open_unique_file3(&working_file_tmp,
+                                   &working_file_tmp_abspath, wc_tmpdir,
+                                   svn_io_file_del_on_pool_cleanup,
+                                   scratch_pool, scratch_pool));
+  working_file_tmp_stream = svn_stream_from_aprfile2(working_file_tmp,
+                                                     FALSE, scratch_pool);
+
+  /* Copy the working file to temporary storage. */
+  SVN_ERR(svn_stream_open_readonly(&working_file_stream, local_abspath,
+                                   scratch_pool, scratch_pool));
+  SVN_ERR(svn_stream_copy3(working_file_stream, working_file_tmp_stream,
+                           ctx->cancel_baton, ctx->cancel_baton,
+                           scratch_pool));
+  SVN_ERR(svn_io_file_flush(working_file_tmp, scratch_pool));
+
+  /* Get a copy of the working file's properties. */
+  SVN_ERR(svn_wc_prop_list2(&working_props, ctx->wc_ctx, local_abspath,
+                            scratch_pool, scratch_pool));
+
+  /* Create a property diff against an empty base. */
+  SVN_ERR(svn_prop_diffs(&propdiffs, apr_hash_make(scratch_pool),
+                         working_props, scratch_pool));
+
+  /* Fetch the incoming added file from the repository. */
+  SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+            &incoming_new_repos_relpath, &incoming_new_pegrev,
+            NULL, conflict, scratch_pool,
+            scratch_pool));
+  SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, &repos_uuid,
+                                             conflict, scratch_pool,
+                                             scratch_pool));
+  url = svn_path_url_add_component2(repos_root_url, incoming_new_repos_relpath,
+                                    scratch_pool);
+  SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
+                                               url, NULL, NULL, FALSE, FALSE,
+                                               conflict->ctx, scratch_pool,
+                                               scratch_pool));
+  if (corrected_url)
+    url = corrected_url;
+  SVN_ERR(svn_io_open_unique_file3(&incoming_new_file, NULL, wc_tmpdir,
+                                   svn_io_file_del_on_pool_cleanup,
+                                   scratch_pool, scratch_pool));
+  incoming_new_stream = svn_stream_from_aprfile2(incoming_new_file, TRUE,
+                                                 scratch_pool);
+  SVN_ERR(svn_ra_get_file(ra_session, "", incoming_new_pegrev,
+                          incoming_new_stream, NULL, /* fetched_rev */
+                          &incoming_new_props, scratch_pool));
+  /* Flush file to disk. */
+  SVN_ERR(svn_io_file_flush(incoming_new_file, scratch_pool));
+
+  /* Reset the stream in preparation for adding its content to WC. */
+  SVN_ERR(svn_stream_reset(incoming_new_stream));
+
+  /* Create an empty file as fake "merge-base" for the two added files.
+   * The files are not ancestrally related so this is the best we can do. */
+  SVN_ERR(svn_io_open_unique_file3(&empty_file, &empty_file_abspath, NULL,
+                                   svn_io_file_del_on_pool_cleanup,
+                                   scratch_pool, scratch_pool));
+
+  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
+                                                 local_abspath,
+                                                 scratch_pool, scratch_pool));
+
+  /* ### The following WC modifications should be atomic. */
+
+  /* Replace the working file with the file from the repository. */
+  err = svn_wc_delete4(ctx->wc_ctx, local_abspath, FALSE, FALSE,
+                       ctx->cancel_func, ctx->cancel_baton,
+                       ctx->notify_func2, ctx->notify_baton2,
+                       scratch_pool);
+  if (err)
+    goto unlock_wc;
+  err = svn_wc_add_repos_file4(ctx->wc_ctx, local_abspath,
+                               incoming_new_stream,
+                               NULL, /* ### could we merge first, then set
+                                        ### the merged content here? */
+                               incoming_new_props,
+                               NULL, /* ### merge props first, set here? */
+                               url, incoming_new_pegrev,
+                               ctx->cancel_func, ctx->cancel_baton,
+                               scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
+                                                     svn_wc_notify_add,
+                                                     scratch_pool);
+      notify->kind = svn_node_file;
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+  /* Resolve to current working copy state. svn_wc_merge5() requires this. */
+  err = svn_wc__del_tree_conflict(ctx->wc_ctx, local_abspath, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Perform the file merge. */
+  err = svn_wc_merge5(&merge_content_outcome, &merge_props_outcome,
+                      ctx->wc_ctx, empty_file_abspath,
+                      working_file_tmp_abspath, local_abspath,
+                      NULL, NULL, NULL, /* labels */
+                      NULL, NULL, /* conflict versions */
+                      FALSE, /* dry run */
+                      NULL, NULL, /* diff3_cmd, merge_options */
+                      NULL, propdiffs,
+                      NULL, NULL, /* conflict func/baton */
+                      ctx->cancel_func, ctx->cancel_baton,
+                      scratch_pool);
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify = svn_wc_create_notify(
+                                   local_abspath,
+                                   svn_wc_notify_update_update,
+                                   scratch_pool);
+
+      if (merge_content_outcome == svn_wc_merge_conflict)
+        notify->content_state = svn_wc_notify_state_conflicted;
+      else
+        notify->content_state = svn_wc_notify_state_merged;
+      notify->prop_state = merge_props_outcome;
+      notify->kind = svn_node_file;
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+
+unlock_wc:
+  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
+                                                                 lock_abspath,
+                                                                 scratch_pool));
+  svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
+  SVN_ERR(err);
+
+  SVN_ERR(svn_stream_close(incoming_new_stream));
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
+                                                     svn_wc_notify_resolved,
+                                                     scratch_pool);
+
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+  conflict->resolution_tree = svn_client_conflict_option_get_id(option);
+
+  return SVN_NO_ERROR;
+}
+
 /* Resolver options for a text conflict */
 static const svn_client_conflict_option_t text_conflict_options[] =
 {
@@ -4147,6 +4336,63 @@ configure_option_merge_incoming_added_fi
   return SVN_NO_ERROR;
 }
 
+/* Configure 'incoming added file replace and merge' resolution option for a
+ * tree conflict. */
+static svn_error_t *
+configure_option_merge_incoming_added_file_replace_and_merge(
+  svn_client_conflict_t *conflict,
+  apr_array_header_t *options,
+  apr_pool_t *scratch_pool)
+{
+  svn_wc_operation_t operation;
+  svn_wc_conflict_action_t incoming_change;
+  svn_wc_conflict_reason_t local_change;
+  svn_node_kind_t victim_node_kind;
+  const char *incoming_new_repos_relpath;
+  svn_revnum_t incoming_new_pegrev;
+  svn_node_kind_t incoming_new_kind;
+
+  operation = svn_client_conflict_get_operation(conflict);
+  incoming_change = svn_client_conflict_get_incoming_change(conflict);
+  local_change = svn_client_conflict_get_local_change(conflict);
+  victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
+  SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+            &incoming_new_repos_relpath, &incoming_new_pegrev,
+            &incoming_new_kind, conflict, scratch_pool,
+            scratch_pool));
+
+  if (operation == svn_wc_operation_merge &&
+      victim_node_kind == svn_node_file &&
+      incoming_new_kind == svn_node_file &&
+      incoming_change == svn_wc_conflict_action_add &&
+      local_change == svn_wc_conflict_reason_obstructed)
+    {
+      svn_client_conflict_option_t *option;
+      const char *wcroot_abspath;
+
+      option = apr_pcalloc(options->pool, sizeof(*option));
+      option->id =
+        svn_client_conflict_option_merge_incoming_added_file_replace_and_merge;
+      SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, conflict->ctx->wc_ctx,
+                                 conflict->local_abspath, scratch_pool,
+                                 scratch_pool));
+      option->description =
+        apr_psprintf(options->pool,
+          _("delete '%s', copy '^/%s@%ld' here, and merge the files"),
+          svn_dirent_local_style(
+            svn_dirent_skip_ancestor(wcroot_abspath,
+                                     conflict->local_abspath),
+            scratch_pool),
+          incoming_new_repos_relpath, incoming_new_pegrev);
+      option->conflict = conflict;
+      option->do_resolve_func =
+        resolve_merge_incoming_added_file_replace_and_merge;
+      APR_ARRAY_PUSH(options, const svn_client_conflict_option_t *) = option;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_client_conflict_tree_get_resolution_options(apr_array_header_t **options,
                                                 svn_client_conflict_t *conflict,
@@ -4178,6 +4424,8 @@ svn_client_conflict_tree_get_resolution_
   SVN_ERR(configure_option_merge_incoming_added_file_text_merge(conflict,
                                                                 *options,
                                                                 scratch_pool));
+  SVN_ERR(configure_option_merge_incoming_added_file_replace_and_merge(
+            conflict, *options, scratch_pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/trunk/subversion/svn/conflict-callbacks.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?rev=1739897&r1=1739896&r2=1739897&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/conflict-callbacks.c (original)
+++ subversion/trunk/subversion/svn/conflict-callbacks.c Tue Apr 19 11:08:37 2016
@@ -417,6 +417,8 @@ static const resolver_option_t builtin_r
   /* Options for incoming file add vs local file add upon merge. */
   { "m", N_("merge the files"), NULL,
     svn_client_conflict_option_merge_incoming_added_file_text_merge },
+  { "M", N_("replace my file with incoming file and merge the files"), NULL,
+    svn_client_conflict_option_merge_incoming_added_file_replace_and_merge },
 
   { NULL }
 };