subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject svn commit: r1745620 - in /subversion/trunk/subversion: include/svn_client.h libsvn_client/conflicts.c svn/conflict-callbacks.c tests/libsvn_client/conflicts-test.c
Date Thu, 26 May 2016 15:46:13 GMT
Author: stsp
Date: Thu May 26 15:46:13 2016
New Revision: 1745620

URL: http://svn.apache.org/viewvc?rev=1745620&view=rev
Log:
Add two new conflict resolution options for incoming added dirs upon merge. 

These options allow the user to replace the local directory with the
incoming directory, and optionally merge the two. Merging, however,
suffers the same problem as the file case. The merge erroneously applies
an empty changeset in some situations (see regression test).

* subversion/include/svn_client.h
  (svn_client_conflict_option_merge_incoming_added_dir_replace,
   svn_client_conflict_option_merge_incoming_added_dir_replace_and_merge):
    New conflict options.

* subversion/libsvn_client/conflicts.c
  (notification_adjust_baton, notification_adjust_func): New helper functions.
   Copied from libsvn_client/copy.c.
  (merge_incoming_added_dir_replace): Implements the new resolution options.
   I've borrowed some code from libsvn_client/copy.c for this.
  (resolve_merge_incoming_added_dir_replace,
   resolve_merge_incoming_added_dir_replace_and_merge): New resolver functions.
  (configure_option_merge_incoming_added_dir_replace,
   configure_option_merge_incoming_added_dir_replace_and_merge): New functions
    to hook up the new resolver functions if applicable.
  (svn_client_conflict_tree_get_resolution_options): Configure new options.

* subversion/svn/conflict-callbacks.c
  (builtin_resolver_options): Hook up the new options.

* subversion/tests/libsvn_client/conflicts-test.c
  (create_wc_with_dir_add_vs_dir_add_merge_conflict): Add an additional
   parameter which, if set, causes a file change to be committed to the
   branch before the merge is run from the trunk to the branch.
   Rename the existing 'file_change' parameter to 'file_change_on_trunk'.
  (test_option_merge_incoming_added_dir_ignore,
   test_option_merge_incoming_added_dir_merge,
   test_option_merge_incoming_added_dir_merge2,
   test_option_merge_incoming_added_dir_merge3): Update callers.
  (test_option_merge_incoming_added_dir_replace,
  (test_option_merge_incoming_added_dir_replace_and_merge,
  (test_option_merge_incoming_added_dir_replace_and_merge2): New tests.
  (test_funcs): Add the new tests.

Modified:
    subversion/trunk/subversion/include/svn_client.h
    subversion/trunk/subversion/libsvn_client/conflicts.c
    subversion/trunk/subversion/svn/conflict-callbacks.c
    subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c

Modified: subversion/trunk/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1745620&r1=1745619&r2=1745620&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Thu May 26 15:46:13 2016
@@ -4416,6 +4416,8 @@ typedef enum svn_client_conflict_option_
 
   /* Options for incoming dir add vs local dir 'obstruction' on merge. */
   svn_client_conflict_option_merge_incoming_added_dir_merge,
+  svn_client_conflict_option_merge_incoming_added_dir_replace,
+  svn_client_conflict_option_merge_incoming_added_dir_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=1745620&r1=1745619&r2=1745620&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_client/conflicts.c Thu May 26 15:46:13 2016
@@ -4122,6 +4122,294 @@ resolve_merge_incoming_added_dir_merge(s
   return SVN_NO_ERROR;
 }
 
+/* A baton for notification_adjust_func(). */
+struct notification_adjust_baton
+{
+  svn_wc_notify_func2_t inner_func;
+  void *inner_baton;
+  const char *checkout_abspath;
+  const char *final_abspath;
+};
+
+/* A svn_wc_notify_func2_t function that wraps BATON->inner_func (whose
+ * baton is BATON->inner_baton) and adjusts the notification paths that
+ * start with BATON->checkout_abspath to start instead with
+ * BATON->final_abspath. */
+static void
+notification_adjust_func(void *baton,
+                         const svn_wc_notify_t *notify,
+                         apr_pool_t *pool)
+{
+  struct notification_adjust_baton *nb = baton;
+  svn_wc_notify_t *inner_notify = svn_wc_dup_notify(notify, pool);
+  const char *relpath;
+
+  relpath = svn_dirent_skip_ancestor(nb->checkout_abspath, notify->path);
+  inner_notify->path = svn_dirent_join(nb->final_abspath, relpath, pool);
+
+  if (nb->inner_func)
+    nb->inner_func(nb->inner_baton, inner_notify, pool);
+}
+
+/* Resolve a dir/dir "incoming add vs local obstruction" tree conflict by
+ * replacing the local directory with the incoming directory.
+ * If MERGE_DIRS is set, also merge the directories after replacing. */
+static svn_error_t *
+merge_incoming_added_dir_replace(svn_client_conflict_option_t *option,
+                                 svn_client_conflict_t *conflict,
+                                 svn_boolean_t merge_dirs,
+                                 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;
+  const char *local_abspath;
+  const char *lock_abspath;
+  svn_client_ctx_t *ctx = conflict->ctx;
+  const char *tmpdir_abspath, *tmp_abspath;
+  svn_error_t *err;
+  svn_revnum_t copy_src_revnum;
+  svn_opt_revision_t copy_src_peg_revision;
+  svn_boolean_t timestamp_sleep;
+  svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2;
+  void *old_notify_baton2 = ctx->notify_baton2;
+  struct notification_adjust_baton nb;
+
+  local_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+  /* Find the URL of the incoming added directory in 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;
+
+
+  /* Find a temporary location in which to check out the copy source. */
+  SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, local_abspath,
+                             scratch_pool, scratch_pool));
+
+  SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath,
+                                   svn_io_file_del_on_close,
+                                   scratch_pool, scratch_pool));
+
+  /* Make a new checkout of the requested source. While doing so,
+   * resolve copy_src_revnum to an actual revision number in case it
+   * was until now 'invalid' meaning 'head'.  Ask this function not to
+   * sleep for timestamps, by passing a sleep_needed output param.
+   * Send notifications for all nodes except the root node, and adjust
+   * them to refer to the destination rather than this temporary path. */
+
+  nb.inner_func = ctx->notify_func2;
+  nb.inner_baton = ctx->notify_baton2;
+  nb.checkout_abspath = tmp_abspath;
+  nb.final_abspath = local_abspath;
+  ctx->notify_func2 = notification_adjust_func;
+  ctx->notify_baton2 = &nb;
+
+  copy_src_peg_revision.kind = svn_opt_revision_number;
+  copy_src_peg_revision.value.number = incoming_new_pegrev;
+
+  err = svn_client__checkout_internal(&copy_src_revnum, &timestamp_sleep,
+                                      url, tmp_abspath,
+                                      &copy_src_peg_revision,
+                                      &copy_src_peg_revision,
+                                      svn_depth_infinity,
+                                      TRUE, /* we want to ignore externals */
+                                      FALSE, /* we don't allow obstructions */
+                                      ra_session, ctx, scratch_pool);
+
+  ctx->notify_func2 = old_notify_func2;
+  ctx->notify_baton2 = old_notify_baton2;
+
+  SVN_ERR(err);
+
+  /* ### The following WC modifications should be atomic. */
+
+  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
+                                                 svn_dirent_dirname(
+                                                   local_abspath,
+                                                   scratch_pool),
+                                                 scratch_pool, scratch_pool));
+
+  /* Remove the working directory. */
+  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;
+
+  /* Schedule dst_path for addition in parent, with copy history.
+     Don't send any notification here.
+     Then remove the temporary checkout's .svn dir in preparation for
+     moving the rest of it into the final destination. */
+  err = svn_wc_copy3(ctx->wc_ctx, tmp_abspath, local_abspath,
+                     TRUE /* metadata_only */,
+                     ctx->cancel_func, ctx->cancel_baton,
+                     NULL, NULL, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  err = svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, tmp_abspath,
+                                   FALSE, scratch_pool, scratch_pool);
+  if (err)
+    goto unlock_wc;
+  err = svn_wc_remove_from_revision_control2(ctx->wc_ctx,
+                                             tmp_abspath,
+                                             FALSE, FALSE,
+                                             ctx->cancel_func,
+                                             ctx->cancel_baton,
+                                             scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Move the temporary disk tree into place. */
+  err = svn_io_file_rename2(tmp_abspath, local_abspath, FALSE, 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_dir;
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+  /* Resolve to current working copy state.
+  * svn_client__merge_locked() requires this. */
+  err = svn_wc__del_tree_conflict(ctx->wc_ctx, local_abspath, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  if (merge_dirs)
+    {
+      svn_client__conflict_report_t *conflict_report;
+      const char *source1;
+      svn_opt_revision_t revision1;
+      const char *source2;
+      svn_opt_revision_t revision2;
+      svn_revnum_t base_revision;
+      const char *base_repos_relpath;
+      struct find_added_rev_baton b;
+
+      /* Find the URL and revision of the directory we have just replaced. */
+      err = svn_wc__node_get_base(NULL, &base_revision, &base_repos_relpath,
+                                  NULL, NULL, NULL, ctx->wc_ctx, local_abspath,
+                                  FALSE, scratch_pool, scratch_pool);
+      if (err)
+        goto unlock_wc;
+
+      url = svn_path_url_add_component2(repos_root_url, base_repos_relpath,
+                                        scratch_pool);
+
+      /* Trace the replaced directory's history to its origin. */
+      err = svn_ra_reparent(ra_session, url, scratch_pool);
+      if (err)
+        goto unlock_wc;
+      b.added_rev = SVN_INVALID_REVNUM;
+      b.repos_relpath = NULL;
+      b.pool = scratch_pool;
+      err = svn_ra_get_location_segments(ra_session, "", base_revision,
+                                         base_revision, SVN_INVALID_REVNUM,
+                                         find_added_rev, &b,
+                                         scratch_pool);
+      if (err)
+        goto unlock_wc;
+
+      if (b.added_rev == SVN_INVALID_REVNUM)
+        {
+          err = svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                                  _("Could not determine the revision in "
+                                    "which '^/%s' was added to the "
+                                    "repository.\n"),
+                                  base_repos_relpath);
+          goto unlock_wc;
+        }
+
+      /* Merge the replaced directory into the directory which replaced it. */
+      source1 = url;
+      revision1.kind = svn_opt_revision_number;
+      revision1.value.number = b.added_rev;
+      source2 = url;
+      revision2.kind = svn_opt_revision_number;
+      revision2.value.number = base_revision;
+      err = svn_client__merge_locked(&conflict_report,
+                                     source1, &revision1,
+                                     source2, &revision2,
+                                     local_abspath, svn_depth_infinity,
+                                     TRUE, TRUE, /* do a no-ancestry merge */
+                                     FALSE, FALSE, FALSE,
+                                     FALSE, /* no need to allow mixed-rev */
+                                     NULL, ctx, scratch_pool, scratch_pool);
+      err = svn_error_compose_create(err,
+                                     svn_client__make_merge_conflict_error(
+                                       conflict_report, 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);
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *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);
+
+  return SVN_NO_ERROR;
+}
+
+/* Implements conflict_option_resolve_func_t. */
+static svn_error_t *
+resolve_merge_incoming_added_dir_replace(svn_client_conflict_option_t *option,
+                                         svn_client_conflict_t *conflict,
+                                         apr_pool_t *scratch_pool)
+{
+  return svn_error_trace(merge_incoming_added_dir_replace(option,
+                                                          conflict,
+                                                          FALSE,
+                                                          scratch_pool));
+}
+
+/* Implements conflict_option_resolve_func_t. */
+static svn_error_t *
+resolve_merge_incoming_added_dir_replace_and_merge(
+  svn_client_conflict_option_t *option,
+  svn_client_conflict_t *conflict,
+  apr_pool_t *scratch_pool)
+{
+  return svn_error_trace(merge_incoming_added_dir_replace(option,
+                                                          conflict,
+                                                          TRUE,
+                                                          scratch_pool));
+}
+
 /* Resolver options for a text conflict */
 static const svn_client_conflict_option_t text_conflict_options[] =
 {
@@ -4753,6 +5041,118 @@ configure_option_merge_incoming_added_di
   return SVN_NO_ERROR;
 }
 
+/* Configure 'incoming added dir replace' resolution option for a tree
+ * conflict. */
+static svn_error_t *
+configure_option_merge_incoming_added_dir_replace(
+  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_dir &&
+      incoming_new_kind == svn_node_dir &&
+      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_dir_replace;
+      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' and copy '^/%s@%ld' here"),
+          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_dir_replace;
+      APR_ARRAY_PUSH(options, const svn_client_conflict_option_t *) = option;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Configure 'incoming added dir replace and merge' resolution option
+ * for a tree conflict. */
+static svn_error_t *
+configure_option_merge_incoming_added_dir_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_dir &&
+      incoming_new_kind == svn_node_dir &&
+      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_dir_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 directories"),
+          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_dir_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,
@@ -4792,6 +5192,10 @@ svn_client_conflict_tree_get_resolution_
             conflict, *options, scratch_pool));
   SVN_ERR(configure_option_merge_incoming_added_dir_merge(conflict, *options,
                                                           scratch_pool));
+  SVN_ERR(configure_option_merge_incoming_added_dir_replace(conflict, *options,
+                                                            scratch_pool));
+  SVN_ERR(configure_option_merge_incoming_added_dir_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=1745620&r1=1745619&r2=1745620&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/conflict-callbacks.c (original)
+++ subversion/trunk/subversion/svn/conflict-callbacks.c Thu May 26 15:46:13 2016
@@ -434,7 +434,10 @@ static const resolver_option_t builtin_r
   /* Options for incoming dir add vs local dir add upon merge. */
   { "m", N_("merge the directories"), NULL,
     svn_client_conflict_option_merge_incoming_added_dir_merge },
-
+  { "R", N_("replace my directory with incoming directory"), NULL,
+    svn_client_conflict_option_merge_incoming_added_dir_replace },
+  { "M", N_("replace my directory with incoming directory and merge"), NULL,
+    svn_client_conflict_option_merge_incoming_added_dir_replace_and_merge },
   { NULL }
 };
 

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=1745620&r1=1745619&r2=1745620&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c Thu May 26 15:46:13 2016
@@ -457,9 +457,11 @@ static const char *new_dir_name = "newdi
 
 /* A helper function which prepares a working copy for the tests below. */
 static svn_error_t *
-create_wc_with_dir_add_vs_dir_add_merge_conflict(svn_test__sandbox_t *b,
-                                                 svn_boolean_t file_change,
-                                                 svn_boolean_t with_move)
+create_wc_with_dir_add_vs_dir_add_merge_conflict(
+  svn_test__sandbox_t *b,
+  svn_boolean_t file_change_on_trunk,
+  svn_boolean_t with_move,
+  svn_boolean_t file_change_on_branch)
 {
   static const char *new_dir_path;
   static const char *new_file_path;
@@ -500,10 +502,11 @@ create_wc_with_dir_add_vs_dir_add_merge_
   SVN_ERR(sbox_wc_add(b, new_file_path));
   SVN_ERR(sbox_wc_propset(b, "prop", propval_trunk, new_file_path));
   SVN_ERR(sbox_wc_commit(b, ""));
-  if (file_change)
+  if (file_change_on_trunk)
     {
       SVN_ERR(sbox_file_write(b, new_file_path,
-                              "This is a change to the new file\n"));
+                              "This is a change to the new file"
+                              "on the trunk\n"));
       SVN_ERR(sbox_wc_commit(b, ""));
     }
   if (with_move)
@@ -529,6 +532,14 @@ create_wc_with_dir_add_vs_dir_add_merge_
   SVN_ERR(sbox_wc_propset(b, "prop", propval_branch, new_file_path));
   SVN_ERR(sbox_wc_commit(b, ""));
 
+  if (file_change_on_branch)
+    {
+      SVN_ERR(sbox_file_write(b, new_file_path,
+                              "This is a change to the new file "
+                              "on the branch\n"));
+      SVN_ERR(sbox_wc_commit(b, ""));
+    }
+
   /* Run a merge from the trunk to the branch. */
   SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
 
@@ -596,7 +607,8 @@ test_option_merge_incoming_added_dir_ign
   SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_ignore",
                                    opts, pool));
 
-  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE));
+  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE,
+                                                           FALSE));
 
   /* Resolve the tree conflict. */
   SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -666,7 +678,8 @@ test_option_merge_incoming_added_dir_mer
   SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge",
                                    opts, pool));
 
-  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE));
+  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE,
+                                                           FALSE));
 
   /* Resolve the tree conflict. */
   SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -758,7 +771,8 @@ test_option_merge_incoming_added_dir_mer
   SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge2",
                                    opts, pool));
 
-  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, FALSE));
+  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, FALSE,
+                                                           FALSE));
 
   /* Resolve the tree conflict. */
   SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -849,7 +863,8 @@ test_option_merge_incoming_added_dir_mer
   SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge3",
                                    opts, pool));
 
-  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, TRUE));
+  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, TRUE,
+                                                           FALSE));
 
   /* Resolve the tree conflict. */
   SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -941,6 +956,244 @@ test_option_merge_incoming_added_dir_mer
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_option_merge_incoming_added_dir_replace(const svn_test_opts_t *opts,
+                                             apr_pool_t *pool)
+{
+  svn_client_ctx_t *ctx;
+  svn_client_conflict_t *conflict;
+  const char *new_dir_path;
+  svn_boolean_t text_conflicted;
+  apr_array_header_t *props_conflicted;
+  svn_boolean_t tree_conflicted;
+  struct status_baton sb;
+  struct svn_client_status_t *status;
+  svn_opt_revision_t opt_rev;
+  svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+
+  SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_replace",
+                                   opts, pool));
+
+  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE,
+                                                           FALSE));
+
+  /* Resolve the tree conflict. */
+  SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+  new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
+  SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+                                  ctx, b->pool, b->pool));
+  SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool));
+  SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+            conflict,
+            svn_client_conflict_option_merge_incoming_added_dir_replace,
+            b->pool));
+
+  /* Ensure that the directory has the expected status. */
+  opt_rev.kind = svn_opt_revision_working;
+  sb.result_pool = b->pool;
+  SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_path),
+                             &opt_rev, svn_depth_unknown, TRUE, TRUE,
+                             TRUE, TRUE, FALSE, TRUE, NULL,
+                             status_func, &sb, b->pool));
+  status = sb.status;
+  SVN_TEST_ASSERT(status->kind == svn_node_dir);
+  SVN_TEST_ASSERT(status->versioned);
+  SVN_TEST_ASSERT(!status->conflicted);
+  SVN_TEST_ASSERT(status->node_status == svn_wc_status_replaced);
+  SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+  SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+  SVN_TEST_ASSERT(status->copied);
+  SVN_TEST_ASSERT(!status->switched);
+  SVN_TEST_ASSERT(!status->file_external);
+  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_dir_path),
+                                  ctx, b->pool, b->pool));
+
+  /* The directory should not be in conflict. */
+  SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+                                             &props_conflicted,
+                                             &tree_conflicted,
+                                             conflict, b->pool, b->pool));
+  SVN_TEST_ASSERT(!text_conflicted &&
+                  props_conflicted->nelts == 0 &&
+                  !tree_conflicted);
+
+  return SVN_NO_ERROR;
+}
+
+/* This test currently fails to meet expectations. Our merge code doesn't
+ * support a merge of files which were added in the same revision as their
+ * parent directory and were not modified since. */
+static svn_error_t *
+test_option_merge_incoming_added_dir_replace_and_merge(
+  const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_client_ctx_t *ctx;
+  svn_client_conflict_t *conflict;
+  const char *new_dir_path;
+  const char *new_file_path;
+  svn_boolean_t text_conflicted;
+  apr_array_header_t *props_conflicted;
+  svn_boolean_t tree_conflicted;
+  struct status_baton sb;
+  struct svn_client_status_t *status;
+  svn_opt_revision_t opt_rev;
+  svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+
+  SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_replace_and_merge",
+                                   opts, pool));
+
+  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE,
+                                                           FALSE));
+
+  /* Resolve the tree conflict. */
+  SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+  new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
+  SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+                                  ctx, b->pool, b->pool));
+  SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool));
+  SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+            conflict,
+            svn_client_conflict_option_merge_incoming_added_dir_replace_and_merge,
+            b->pool));
+
+  /* Ensure that the directory has the expected status. */
+  opt_rev.kind = svn_opt_revision_working;
+  sb.result_pool = b->pool;
+  SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_path),
+                             &opt_rev, svn_depth_unknown, TRUE, TRUE,
+                             TRUE, TRUE, FALSE, TRUE, NULL,
+                             status_func, &sb, b->pool));
+  status = sb.status;
+  SVN_TEST_ASSERT(status->kind == svn_node_dir);
+  SVN_TEST_ASSERT(status->versioned);
+  SVN_TEST_ASSERT(!status->conflicted);
+  SVN_TEST_ASSERT(status->node_status == svn_wc_status_replaced);
+  SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+  SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+  SVN_TEST_ASSERT(status->copied);
+  SVN_TEST_ASSERT(!status->switched);
+  SVN_TEST_ASSERT(!status->file_external);
+  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_dir_path),
+                                  ctx, b->pool, b->pool));
+
+  /* The directory should not be in conflict. */
+  SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+                                             &props_conflicted,
+                                             &tree_conflicted,
+                                             conflict, b->pool, b->pool));
+  SVN_TEST_ASSERT(!text_conflicted &&
+                  props_conflicted->nelts == 0 &&
+                  !tree_conflicted);
+
+  /* We should have a text conflict in the file. */
+  new_file_path = svn_relpath_join(branch_path,
+                                   svn_relpath_join(new_dir_name,
+                                                    new_file_name, b->pool),
+                                   b->pool);
+  SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
+                                  ctx, b->pool, b->pool));
+  SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+                                             &props_conflicted,
+                                             &tree_conflicted,
+                                             conflict, b->pool, b->pool));
+  SVN_TEST_ASSERT(text_conflicted &&
+                  props_conflicted->nelts == 0 &&
+                  !tree_conflicted);
+
+  return SVN_NO_ERROR;
+}
+
+/* Same test as above, but with an additional file change on the branch
+ * which makes resolution work as expected. */
+static svn_error_t *
+test_option_merge_incoming_added_dir_replace_and_merge2(
+  const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_client_ctx_t *ctx;
+  svn_client_conflict_t *conflict;
+  const char *new_dir_path;
+  const char *new_file_path;
+  svn_boolean_t text_conflicted;
+  apr_array_header_t *props_conflicted;
+  svn_boolean_t tree_conflicted;
+  struct status_baton sb;
+  struct svn_client_status_t *status;
+  svn_opt_revision_t opt_rev;
+  svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+
+  SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_replace_and_merge",
+                                   opts, pool));
+
+  SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE,
+                                                           TRUE));
+
+  /* Resolve the tree conflict. */
+  SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+  new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
+  SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+                                  ctx, b->pool, b->pool));
+  SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool));
+  SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+            conflict,
+            svn_client_conflict_option_merge_incoming_added_dir_replace_and_merge,
+            b->pool));
+
+  /* Ensure that the directory has the expected status. */
+  opt_rev.kind = svn_opt_revision_working;
+  sb.result_pool = b->pool;
+  SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_path),
+                             &opt_rev, svn_depth_unknown, TRUE, TRUE,
+                             TRUE, TRUE, FALSE, TRUE, NULL,
+                             status_func, &sb, b->pool));
+  status = sb.status;
+  SVN_TEST_ASSERT(status->kind == svn_node_dir);
+  SVN_TEST_ASSERT(status->versioned);
+  SVN_TEST_ASSERT(!status->conflicted);
+  SVN_TEST_ASSERT(status->node_status == svn_wc_status_replaced);
+  SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+  SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+  SVN_TEST_ASSERT(status->copied);
+  SVN_TEST_ASSERT(!status->switched);
+  SVN_TEST_ASSERT(!status->file_external);
+  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_dir_path),
+                                  ctx, b->pool, b->pool));
+
+  /* The directory should not be in conflict. */
+  SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+                                             &props_conflicted,
+                                             &tree_conflicted,
+                                             conflict, b->pool, b->pool));
+  SVN_TEST_ASSERT(!text_conflicted &&
+                  props_conflicted->nelts == 0 &&
+                  !tree_conflicted);
+
+  /* We should have a text conflict in the file. */
+  new_file_path = svn_relpath_join(branch_path,
+                                   svn_relpath_join(new_dir_name,
+                                                    new_file_name, b->pool),
+                                   b->pool);
+  SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
+                                  ctx, b->pool, b->pool));
+  SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+                                             &props_conflicted,
+                                             &tree_conflicted,
+                                             conflict, b->pool, b->pool));
+  SVN_TEST_ASSERT(text_conflicted &&
+                  props_conflicted->nelts == 0 &&
+                  !tree_conflicted);
+
+  return SVN_NO_ERROR;
+}
+
 /* ========================================================================== */
 
 
@@ -965,6 +1218,12 @@ static struct svn_test_descriptor_t test
                        "test incoming add dir merge with file change"),
     SVN_TEST_OPTS_XFAIL(test_option_merge_incoming_added_dir_merge3,
                        "test incoming add dir merge with move history"),
+    SVN_TEST_OPTS_PASS(test_option_merge_incoming_added_dir_replace,
+                       "test incoming add dir replace"),
+    SVN_TEST_OPTS_XFAIL(test_option_merge_incoming_added_dir_replace_and_merge,
+                       "test incoming add dir replace and merge"),
+    SVN_TEST_OPTS_PASS(test_option_merge_incoming_added_dir_replace_and_merge2,
+                       "test incoming add dir replace and merge"),
     SVN_TEST_NULL
   };
 




Mime
View raw message