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/11/19 14:14:21 UTC

svn commit: r1770496 - in /subversion/trunk/subversion: libsvn_client/conflicts.c tests/libsvn_client/conflicts-test.c

Author: stsp
Date: Sat Nov 19 14:14:21 2016
New Revision: 1770496

URL: http://svn.apache.org/viewvc?rev=1770496&view=rev
Log:
Implement conflict option 'merge files' for 'add file vs add file' upon update.

* subversion/libsvn_client/conflicts.c
  (resolve_merge_incoming_added_file_text_update): New helper function.
    This function keeps the repository's line of history by undoing the
    replacement scheduled while the tree conflict was raised.
  (configure_option_incoming_added_file_text_merge): Configure new option.

* subversion/tests/libsvn_client/conflicts-test.c
  (test_update_incoming_added_file_text_merge): Update test expectations.
   In particular, stop expecting the replace_and_merge option for updates
   since such an option makes no sense as there is only one line of history
   to choose from. A valid choice would be replace the file entirely to create
   a new line of history, but that should be handled by another option.
  (test_funcs): Mark test_update_incoming_added_file_text_merge as PASS.

Modified:
    subversion/trunk/subversion/libsvn_client/conflicts.c
    subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c

Modified: subversion/trunk/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/conflicts.c?rev=1770496&r1=1770495&r2=1770496&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_client/conflicts.c Sat Nov 19 14:14:21 2016
@@ -5235,6 +5235,144 @@ unlock_wc:
 
   return SVN_NO_ERROR;
 }
+
+/* Implements conflict_option_resolve_func_t. */
+static svn_error_t *
+resolve_merge_incoming_added_file_text_update(
+  svn_client_conflict_option_t *option,
+  svn_client_conflict_t *conflict,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool)
+{
+  const char *wc_tmpdir;
+  const char *local_abspath;
+  const char *lock_abspath;
+  svn_wc_merge_outcome_t merge_content_outcome;
+  svn_wc_notify_state_t merge_props_outcome;
+  const char *empty_file_abspath;
+  const char *working_file_tmp_abspath;
+  svn_stream_t *working_file_stream;
+  svn_stream_t *working_file_tmp_stream;
+  apr_hash_t *working_props;
+  apr_hash_index_t *hi;
+  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_stream_open_unique(&working_file_tmp_stream,
+                                 &working_file_tmp_abspath, wc_tmpdir,
+                                 /* Don't delete automatically! */
+                                 svn_io_file_del_none,
+                                 scratch_pool, 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_func, ctx->cancel_baton,
+                           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));
+
+  /* Delete entry and wc props from the returned set of properties.. */
+  for (hi = apr_hash_first(scratch_pool, working_props);
+       hi != NULL;
+       hi = apr_hash_next(hi))
+    {
+      const char *propname = apr_hash_this_key(hi);
+
+      if (!svn_wc_is_normal_prop(propname))
+        svn_hash_sets(working_props, propname, NULL);
+    }
+
+  /* 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(NULL, &empty_file_abspath, NULL,
+                                   svn_io_file_del_on_pool_cleanup,
+                                   scratch_pool, scratch_pool));
+
+  /* Create a property diff which shows all props as added. */
+  SVN_ERR(svn_prop_diffs(&propdiffs, working_props,
+                         apr_hash_make(scratch_pool), scratch_pool));
+
+  /* ### The following WC modifications should be atomic. */
+  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
+                                                 local_abspath,
+                                                 scratch_pool, scratch_pool));
+
+  /* Revert the path in order to restore the repository's line of
+   * history, which is part of the BASE tree. This revert operation
+   * is why are being careful about not losing the temporary copy. */
+  err = svn_wc_revert5(ctx->wc_ctx, local_abspath, svn_depth_empty,
+                       FALSE, NULL, TRUE, FALSE,
+                       NULL, NULL, /* no cancellation */
+                       ctx->notify_func2, ctx->notify_baton2,
+                       scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Perform the file merge. ### Merge into tempfile and then rename on top? */
+  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 */
+                      NULL, NULL, /* don't allow user to cancel here */
+                      scratch_pool);
+
+unlock_wc:
+  if (err)
+      err = svn_error_quick_wrapf(
+              err, _("If needed, a backup copy of '%s' can be found at '%s'"),
+              svn_dirent_local_style(local_abspath, scratch_pool),
+              svn_dirent_local_style(working_file_tmp_abspath, scratch_pool));
+  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);
+  
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      /* Tell the world about the file merge that just happened. */
+      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);
+
+      /* And also about the successfully resolved tree conflict. */
+      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_resolved_tree,
+                                    scratch_pool);
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+  conflict->resolution_tree = svn_client_conflict_option_get_id(option);
+
+  /* All is good -- remove temporary copy of the working file. */
+  SVN_ERR(svn_io_remove_file2(working_file_tmp_abspath, TRUE, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Implements conflict_option_resolve_func_t. */
 static svn_error_t *
 resolve_merge_incoming_added_file_text_merge(
@@ -7773,11 +7911,11 @@ configure_option_incoming_added_file_tex
             &incoming_new_kind, conflict, scratch_pool,
             scratch_pool));
 
-  if (operation == svn_wc_operation_merge &&
-      victim_node_kind == svn_node_file &&
+  if (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)
+      (local_change == svn_wc_conflict_reason_obstructed ||
+       local_change == svn_wc_conflict_reason_added))
     {
       const char *description;
       const char *wcroot_abspath;
@@ -7785,19 +7923,31 @@ configure_option_incoming_added_file_tex
       SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
                                  conflict->local_abspath, scratch_pool,
                                  scratch_pool));
-      description =
-        apr_psprintf(scratch_pool, _("merge '^/%s@%ld' into '%s'"),
-          incoming_new_repos_relpath, incoming_new_pegrev,
-          svn_dirent_local_style(
-            svn_dirent_skip_ancestor(wcroot_abspath,
-                                     conflict->local_abspath),
-            scratch_pool));
+
+      if (operation == svn_wc_operation_merge)
+        description =
+          apr_psprintf(scratch_pool, _("merge '^/%s@%ld' into '%s'"),
+            incoming_new_repos_relpath, incoming_new_pegrev,
+            svn_dirent_local_style(
+              svn_dirent_skip_ancestor(wcroot_abspath,
+                                       conflict->local_abspath),
+              scratch_pool));
+      else
+        description =
+          apr_psprintf(scratch_pool, _("merge local '%s' and '^/%s@%ld'"),
+            svn_dirent_local_style(
+              svn_dirent_skip_ancestor(wcroot_abspath,
+                                       conflict->local_abspath),
+              scratch_pool),
+            incoming_new_repos_relpath, incoming_new_pegrev);
 
       add_resolution_option(
         options, conflict,
         svn_client_conflict_option_incoming_added_file_text_merge,
         _("Merge the files"), description,
-        resolve_merge_incoming_added_file_text_merge);
+        operation == svn_wc_operation_merge
+          ? resolve_merge_incoming_added_file_text_merge
+          : resolve_merge_incoming_added_file_text_update);
     }
 
   return SVN_NO_ERROR;

Modified: subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c?rev=1770496&r1=1770495&r2=1770496&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c Sat Nov 19 14:14:21 2016
@@ -3394,6 +3394,7 @@ test_update_incoming_added_file_text_mer
     svn_client_conflict_option_id_t expected_opts[] = {
       svn_client_conflict_option_postpone,
       svn_client_conflict_option_accept_current_wc_state,
+      svn_client_conflict_option_incoming_added_file_text_merge,
       -1 /* end of list */
     };
     SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
@@ -3401,14 +3402,13 @@ test_update_incoming_added_file_text_mer
 
   SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, pool));
 
-  /* Check available tree conflict resolution options. */
-  /* XFAIL: The list of options remains unchanged after get_details(). */
+  /* Check available tree conflict resolution options.
+   * The list of options remains unchanged after get_details(). */
   {
     svn_client_conflict_option_id_t expected_opts[] = {
       svn_client_conflict_option_postpone,
       svn_client_conflict_option_accept_current_wc_state,
       svn_client_conflict_option_incoming_added_file_text_merge,
-      svn_client_conflict_option_incoming_added_file_replace_and_merge,
       -1 /* end of list */
     };
     SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
@@ -3430,7 +3430,7 @@ test_update_incoming_added_file_text_mer
   SVN_TEST_ASSERT(status->kind == svn_node_file);
   SVN_TEST_ASSERT(status->versioned);
   SVN_TEST_ASSERT(status->conflicted);
-  SVN_TEST_ASSERT(status->node_status == svn_wc_status_modified);
+  SVN_TEST_ASSERT(status->node_status == svn_wc_status_conflicted);
   SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
   SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified);
   SVN_TEST_ASSERT(!status->copied);
@@ -3439,6 +3439,7 @@ test_update_incoming_added_file_text_mer
   SVN_TEST_ASSERT(status->moved_from_abspath == NULL);
   SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
 
+
   SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
                                   ctx, b->pool, b->pool));
 
@@ -3761,7 +3762,7 @@ static struct svn_test_descriptor_t test
                        "merge incoming file move with new line of history"),
     SVN_TEST_OPTS_XFAIL(test_update_incoming_dir_move_with_nested_file_move,
                        "update incoming dir move with nested file move"),
-    SVN_TEST_OPTS_XFAIL(test_update_incoming_added_file_text_merge,
+    SVN_TEST_OPTS_PASS(test_update_incoming_added_file_text_merge,
                        "update incoming add file text merge"),
     SVN_TEST_OPTS_XFAIL(test_merge_incoming_move_file_prop_merge_conflict,
                         "merge incoming move file merge with prop conflict"),