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 2012/10/18 16:38:58 UTC

svn commit: r1399679 - in /subversion/trunk/subversion: libsvn_wc/conflicts.c libsvn_wc/conflicts.h libsvn_wc/tree_conflict_editor.c svn/conflict-callbacks.c

Author: stsp
Date: Thu Oct 18 14:38:57 2012
New Revision: 1399679

URL: http://svn.apache.org/viewvc?rev=1399679&view=rev
Log:
Work-in-progress commit of an editor to update moved-away tree conflict victims.

I'm committing this code to trunk with hopes of getting it into releasable
shape for 1.8.

* subversion/libsvn_wc/conflicts.c
  (resolve_conflict_on_node): Make work_items a parameter instead of a local
   variable. The tree-conflict editor can accumulate work items that need
   to run when the conflict is marked resolved, and this function runs them. 
  (svn_wc__resolve_text_conflict): Pass NULL work to resolve_conflict_on_node().
  (conflict_status_walker): Accept 'mine-conflict' and 'theirs-conflict'
   choices for resolving tree-conflicts flagged by update operations
   which were caused by a locally moved-away node. For 'mine-conflict',
   involve the tree conflict editor to update the moved-away subtree.
   For 'theirs-conflict', do nothing for now (the plan is to break the move).
   Pass NULL work to resolve_conflict_on_node() where appropriate.

* subversion/libsvn_wc/conflicts.h
  (svn_wc__update_moved_away_conflict_victim): Declare.

* subversion/libsvn_wc/tree_conflict_editor.c: New file. Contains scaffold
   of an Ev2 editor that updates moved-away subtrees as part of tree-conflict
   resolution. The editor does not update moved-away nodes in the NODES
   table yet. But it already merges file content changes into moved-away
   files, which is a starting point.

* subversion/svn/conflict-callbacks.c
  (svn_cl__conflict_func_interactive): Offer 'theirs-conflict' and
   'mine-conflict' choices for tree conflicts. Once the final 1.8 feature
   set of the tree-conflict editor is known we should only offer these
   options in scenarios the editor supports.

Added:
    subversion/trunk/subversion/libsvn_wc/tree_conflict_editor.c   (with props)
Modified:
    subversion/trunk/subversion/libsvn_wc/conflicts.c
    subversion/trunk/subversion/libsvn_wc/conflicts.h
    subversion/trunk/subversion/svn/conflict-callbacks.c

Modified: subversion/trunk/subversion/libsvn_wc/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/conflicts.c?rev=1399679&r1=1399678&r2=1399679&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_wc/conflicts.c Thu Oct 18 14:38:57 2012
@@ -2243,6 +2243,7 @@ resolve_conflict_on_node(svn_boolean_t *
                          svn_boolean_t resolve_props,
                          svn_boolean_t resolve_tree,
                          svn_wc_conflict_choice_t conflict_choice,
+                         svn_skel_t *work_items,
                          svn_cancel_func_t cancel_func_t,
                          void *cancel_baton,
                          apr_pool_t *scratch_pool)
@@ -2252,7 +2253,6 @@ resolve_conflict_on_node(svn_boolean_t *
   svn_boolean_t text_conflicted;
   svn_boolean_t prop_conflicted;
   svn_boolean_t tree_conflicted;
-  svn_skel_t *work_items = NULL;
   svn_skel_t *work_item;
   apr_pool_t *pool = scratch_pool;
 
@@ -2542,6 +2542,7 @@ svn_wc__resolve_text_conflict(svn_wc__db
                            FALSE /* resolve_props */,
                            FALSE /* resolve_tree */,
                            svn_wc_conflict_choose_merged,
+                           NULL,
                            NULL, NULL, /* cancel_func */
                            scratch_pool));
 }
@@ -2591,6 +2592,8 @@ conflict_status_walker(void *baton,
       const svn_wc_conflict_description2_t *cd;
       svn_boolean_t did_resolve;
       svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
+      svn_skel_t *work_items = NULL;
+
 
       cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
 
@@ -2621,15 +2624,31 @@ conflict_status_walker(void *baton,
             if (!cswb->resolve_tree)
               break;
 
-            /* For now, we only clear tree conflict information and resolve
-             * to the working state. There is no way to pick theirs-full
-             * or mine-full, etc. Throw an error if the user expects us
-             * to be smarter than we really are. */
-            if (my_choice != svn_wc_conflict_choose_merged)
+            /* After updates, we can resolve local moved-away vs. any incoming
+             * change, either by updating the moved-away node (mine-conflict)
+             * or by breaking the move (theirs-conflict). */
+            if ((cd->operation == svn_wc_operation_update ||
+                 cd->operation == svn_wc_operation_switch) &&
+                cd->reason == svn_wc_conflict_reason_moved_away)
+              {
+                if (my_choice == svn_wc_conflict_choose_mine_conflict)
+                  SVN_ERR(svn_wc__update_moved_away_conflict_victim(
+                            &work_items, local_abspath, cswb->db,
+                            cswb->cancel_func, cswb->cancel_baton,
+                            scratch_pool));
+                 else if (my_choice == svn_wc_conflict_choose_theirs_conflict)
+                  {
+                    /* ### TODO break move */
+                  }
+              }
+            else if (my_choice != svn_wc_conflict_choose_merged)
               {
+                /* For other tree conflicts, there is no way to pick
+                 * theirs-full or mine-full, etc. Throw an error if the
+                 * user expects us to be smarter than we really are. */
                 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
                                          NULL,
-                                         _("Tree conflicts can only be "
+                                         _("Tree conflict can only be "
                                            "resolved to 'working' state; "
                                            "'%s' not resolved"),
                                          svn_dirent_local_style(local_abspath,
@@ -2643,6 +2662,7 @@ conflict_status_walker(void *baton,
                                              FALSE /* resolve_props */,
                                              TRUE /* resolve_tree */,
                                              my_choice,
+                                             work_items,
                                              cswb->cancel_func,
                                              cswb->cancel_baton,
                                              iterpool));
@@ -2661,6 +2681,7 @@ conflict_status_walker(void *baton,
                                              FALSE /* resolve_props */,
                                              FALSE /* resolve_tree */,
                                              my_choice,
+                                             NULL,
                                              cswb->cancel_func,
                                              cswb->cancel_baton,
                                              iterpool));
@@ -2690,6 +2711,7 @@ conflict_status_walker(void *baton,
                                              TRUE /* resolve_props */,
                                              FALSE /* resolve_tree */,
                                              my_choice,
+                                             NULL,
                                              cswb->cancel_func,
                                              cswb->cancel_baton,
                                              iterpool));

Modified: subversion/trunk/subversion/libsvn_wc/conflicts.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/conflicts.h?rev=1399679&r1=1399678&r2=1399679&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/conflicts.h (original)
+++ subversion/trunk/subversion/libsvn_wc/conflicts.h Thu Oct 18 14:38:57 2012
@@ -394,6 +394,17 @@ svn_wc__resolve_text_conflict(svn_wc__db
                               const char *local_abspath,
                               apr_pool_t *scratch_pool);
 
+/* Update a moved-away tree conflict victim at VICTIM_ABSPATH with changes
+ * brought in by the update operation which flagged the tree conflict.
+ * Set *WORK_ITEMS to a list of work items that need to run as part
+ * of marking the conflict resolved. */
+svn_error_t *
+svn_wc__update_moved_away_conflict_victim(svn_skel_t **work_items,
+                                          const char *victim_abspath,
+                                          svn_wc__db_t *db,
+                                          svn_cancel_func_t cancel_func,
+                                          void *cancel_baton,
+                                          apr_pool_t *scratch_pool);
 
 #ifdef __cplusplus
 }

Added: subversion/trunk/subversion/libsvn_wc/tree_conflict_editor.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/tree_conflict_editor.c?rev=1399679&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/tree_conflict_editor.c (added)
+++ subversion/trunk/subversion/libsvn_wc/tree_conflict_editor.c Thu Oct 18 14:38:57 2012
@@ -0,0 +1,633 @@
+/*
+ * tree_conflict_editor.c :  editing trees during tree-conflict resolution
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+/* This editor is used during resolution of tree conflicts.
+ *
+ * An operation such as update can produce incoming changes for a
+ * locally moved-away subtree, causing a tree-conflict to be flagged.
+ * This editor transfers these changes from the moved-away part of the
+ * working copy to the corresponding moved-here part of the working copy.
+ * 
+ * Both the driver and receiver components of the editor are implemented
+ * in this file.
+ */
+
+#include "svn_checksum.h"
+#include "svn_dirent_uri.h"
+#include "svn_editor.h"
+#include "svn_error.h"
+#include "svn_wc.h"
+#include "svn_pools.h"
+
+#include "private/svn_skel.h"
+#include "private/svn_sqlite.h"
+#include "private/svn_wc_private.h"
+
+#include "libsvn_wc/wc.h"
+#include "libsvn_wc/wc_db.h"
+#include "libsvn_wc/conflicts.h"
+#include "libsvn_wc/workqueue.h"
+
+/*
+ * Receiver code.
+ */
+
+struct tc_editor_baton {
+  const char *src_abspath;
+  const char *dst_abspath;
+  svn_wc__db_t *db;
+  svn_skel_t **work_items;
+  svn_wc_conflict_version_t *old_version;
+  svn_wc_conflict_version_t *new_version;
+} tc_editor_baton;
+
+static svn_error_t *
+tc_editor_add_directory(void *baton,
+                        const char *relpath,
+                        const apr_array_header_t *children,
+                        apr_hash_t *props,
+                        svn_revnum_t replaces_rev,
+                        apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_add_file(void *baton,
+                   const char *relpath,
+                   const svn_checksum_t *checksum,
+                   svn_stream_t *contents,
+                   apr_hash_t *props,
+                   svn_revnum_t replaces_rev,
+                   apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_add_symlink(void *baton,
+                      const char *relpath,
+                      const char *target,
+                      apr_hash_t *props,
+                      svn_revnum_t replaces_rev,
+                      apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_add_absent(void *baton,
+                     const char *relpath,
+                     svn_kind_t kind,
+                     svn_revnum_t replaces_rev,
+                     apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_alter_directory(void *baton,
+                          const char *relpath,
+                          svn_revnum_t revision,
+                          const apr_array_header_t *children,
+                          apr_hash_t *props,
+                          apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_alter_file(void *baton,
+                     const char *dst_relpath,
+                     svn_revnum_t expected_moved_here_revision,
+                     apr_hash_t *props,
+                     const svn_checksum_t *moved_away_checksum,
+                     svn_stream_t *post_update_contents,
+                     apr_pool_t *scratch_pool)
+{
+  struct tc_editor_baton *b = baton;
+  const char *moved_to_abspath = svn_dirent_join(b->dst_abspath,
+                                                 dst_relpath, scratch_pool);
+  const svn_checksum_t *moved_here_checksum;
+  const char *original_repos_relpath;
+  svn_revnum_t original_revision;
+  svn_kind_t kind;
+
+  SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, &moved_here_checksum, NULL,
+                               &original_repos_relpath, NULL, NULL,
+                               &original_revision, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, b->db, moved_to_abspath,
+                               scratch_pool, scratch_pool));
+  SVN_ERR_ASSERT(original_revision == expected_moved_here_revision);
+
+  /* ### check original revision against moved-here op-root revision? */
+  if (kind != svn_kind_file)
+    return SVN_NO_ERROR;
+
+  /* ### what if checksum kind differs?*/
+  if (!svn_checksum_match(moved_away_checksum, moved_here_checksum))
+    {
+      const char *pre_update_pristine_abspath;
+      const char *post_update_pristine_abspath;
+      svn_skel_t *conflict_skel;
+      enum svn_wc_merge_outcome_t merge_outcome;
+
+      /* ### notify */
+
+      /* 
+       * Run a 3-way merge to update the file, using the pre-update
+       * pristine text as the merge base, the post-update pristine
+       * text as the merge-left version, and the current content of the
+       * moved-here working file as the merge-right version.
+       */
+      SVN_ERR(svn_wc__db_pristine_get_path(&pre_update_pristine_abspath,
+                                           b->db, moved_to_abspath,
+                                           moved_here_checksum,
+                                           scratch_pool, scratch_pool));
+      SVN_ERR(svn_wc__db_pristine_get_path(&post_update_pristine_abspath,
+                                           b->db, moved_to_abspath,
+                                           moved_away_checksum,
+                                           scratch_pool, scratch_pool));
+      SVN_ERR(svn_wc__internal_merge(b->work_items, &conflict_skel,
+                                     &merge_outcome, b->db,
+                                     pre_update_pristine_abspath,
+                                     post_update_pristine_abspath,
+                                     moved_to_abspath,
+                                     moved_to_abspath,
+                                     NULL, NULL, NULL, /* diff labels */
+                                     NULL, /* actual props */
+                                     FALSE, /* dry-run */
+                                     NULL, /* diff3-cmd */
+                                     NULL, /* merge options */
+                                     NULL, /* prop_diff */
+                                     NULL, NULL, /* cancel_func + baton */
+                                     scratch_pool, scratch_pool));
+
+      if (merge_outcome == svn_wc_merge_conflict && conflict_skel)
+        {
+          svn_skel_t *work_item;
+          svn_wc_conflict_version_t *original_version;
+
+          original_version = svn_wc_conflict_version_dup(b->old_version,
+                                                         scratch_pool);
+          original_version->path_in_repos = original_repos_relpath;
+          original_version->node_kind = svn_node_file;
+          SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel,
+                                                      original_version,
+                                                      scratch_pool,
+                                                      scratch_pool));
+          SVN_ERR(svn_wc__conflict_create_markers(&work_item, b->db,
+                                                  moved_to_abspath,
+                                                  conflict_skel,
+                                                  scratch_pool,
+                                                  scratch_pool));
+          *b->work_items = svn_wc__wq_merge(*b->work_items, work_item,
+                                            scratch_pool);
+        }
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tc_editor_alter_symlink(void *baton,
+                        const char *relpath,
+                        svn_revnum_t revision,
+                        apr_hash_t *props,
+                        const char *target,
+                        apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_delete(void *baton,
+                 const char *relpath,
+                 svn_revnum_t revision,
+                 apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_copy(void *baton,
+               const char *src_relpath,
+               svn_revnum_t src_revision,
+               const char *dst_relpath,
+               svn_revnum_t replaces_rev,
+               apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_move(void *baton,
+               const char *src_relpath,
+               svn_revnum_t src_revision,
+               const char *dst_relpath,
+               svn_revnum_t replaces_rev,
+               apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_rotate(void *baton,
+                 const apr_array_header_t *relpaths,
+                 const apr_array_header_t *revisions,
+                 apr_pool_t *scratch_pool)
+{
+  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+}
+
+static svn_error_t *
+tc_editor_complete(void *baton,
+                   apr_pool_t *scratch_pool)
+{
+  /* ### notify */
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tc_editor_abort(void *baton,
+                apr_pool_t *scratch_pool)
+{
+  return SVN_NO_ERROR;
+}
+
+static const svn_editor_cb_many_t editor_ops = {
+  tc_editor_add_directory,
+  tc_editor_add_file,
+  tc_editor_add_symlink,
+  tc_editor_add_absent,
+  tc_editor_alter_directory,
+  tc_editor_alter_file,
+  tc_editor_alter_symlink,
+  tc_editor_delete,
+  tc_editor_copy,
+  tc_editor_move,
+  tc_editor_rotate,
+  tc_editor_complete,
+  tc_editor_abort
+};
+
+
+/*
+ * Driver code.
+ */
+
+static svn_error_t *
+get_tc_info(svn_wc_operation_t *operation,
+            svn_wc_conflict_reason_t *local_change,
+            svn_wc_conflict_action_t *incoming_change,
+            svn_wc_conflict_version_t **old_version,
+            svn_wc_conflict_version_t **new_version,
+            const char *src_abspath,
+            const char *dst_abspath,
+            svn_wc__db_t *db,
+            apr_pool_t *result_pool,
+            apr_pool_t *scratch_pool)
+{
+  const apr_array_header_t *locations;
+  svn_boolean_t tree_conflicted;
+  svn_skel_t *conflict_skel;
+  svn_kind_t kind;
+
+  /* ### Check for mixed-rev src or dst? */
+
+  /* Check for tree conflict on src. */
+  SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db,
+                                   src_abspath,
+                                   scratch_pool, scratch_pool));
+  if (!conflict_skel)
+    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                             _("'%s' is not in conflict"),
+                             svn_dirent_local_style(src_abspath,
+                                                    scratch_pool));
+
+  SVN_ERR(svn_wc__conflict_read_info(operation, &locations,
+                                     NULL, NULL, &tree_conflicted,
+                                     db, src_abspath,
+                                     conflict_skel, result_pool,
+                                     scratch_pool));
+  if (!tree_conflicted)
+    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                             _("'%s' is not a tree-conflict victim"),
+                             svn_dirent_local_style(src_abspath,
+                                                    scratch_pool));
+  if (locations)
+    {
+      *old_version = APR_ARRAY_IDX(locations, 0,
+                                     svn_wc_conflict_version_t *);
+      if (locations->nelts > 1)
+        *new_version = APR_ARRAY_IDX(locations, 1,
+                                     svn_wc_conflict_version_t *);
+      else
+        {
+          const char *repos_root_url;
+          const char *repos_uuid;
+          const char *repos_relpath;
+          svn_revnum_t revision;
+          svn_node_kind_t node_kind;
+
+          /* Construct b->new_version from BASE info. */
+          SVN_ERR(svn_wc__db_base_get_info(NULL, &kind, &revision,
+                                           &repos_relpath, &repos_root_url,
+                                           &repos_uuid, NULL, NULL, NULL,
+                                           NULL, NULL, NULL, NULL, NULL, NULL,
+                                           db, src_abspath, result_pool,
+                                           scratch_pool));
+          node_kind = svn__node_kind_from_kind(kind);
+          *new_version = svn_wc_conflict_version_create2(repos_root_url,
+                                                         repos_uuid,
+                                                         repos_relpath,
+                                                         revision,
+                                                         node_kind,
+                                                         scratch_pool);
+        }
+    }
+
+  SVN_ERR(svn_wc__conflict_read_tree_conflict(local_change,
+                                              incoming_change,
+                                              db, src_abspath,
+                                              conflict_skel, scratch_pool,
+                                              scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+update_moved_away_file(svn_editor_t *tc_editor,
+                       const char *src_abspath,
+                       const char *moved_to_abspath,
+                       const char *move_dst_op_root_abspath,
+                       svn_wc__db_t *db,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
+{
+  svn_wc__db_status_t status;
+  const char *op_root_abspath;
+  const char *moved_from_abspath;
+  svn_kind_t kind;
+  svn_revnum_t moved_here_revision;
+  svn_stream_t *post_update_contents;
+  const svn_checksum_t *moved_away_checksum;
+  const char *dst_relpath;
+
+  /* 
+   * ### Currently doesn't work right if the moved-away node has been replaced.
+   * ### Need to read info from the move op-root's op-depth, not WORKING, to
+   * ### properly update shadowed nodes within multi-layer move destinations.
+   * ### Requires a new wc_db API.
+   */
+
+  /* Is this moved-here node part of our move operation? */
+  SVN_ERR(svn_wc__db_scan_addition(&status, &op_root_abspath, NULL, NULL,
+                                   NULL, NULL, NULL, NULL, NULL,
+                                   &moved_from_abspath, NULL,
+                                   db, moved_to_abspath,
+                                   scratch_pool, scratch_pool));
+  if (status != svn_wc__db_status_moved_here ||
+      strcmp(moved_from_abspath, src_abspath) != 0 ||
+      strcmp(move_dst_op_root_abspath, op_root_abspath) != 0)
+    return SVN_NO_ERROR;
+
+  /* Get kind, revision, and checksum of the moved-here node. */
+  SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, &moved_here_revision, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               db, moved_to_abspath, scratch_pool,
+                               scratch_pool));
+  /* ### check original revision against moved-here op-root revision? */
+  if (kind != svn_kind_file)
+    return SVN_NO_ERROR;
+
+  /* Read post-update contents from the updated moved-away file and tell
+   * the editor to merge them into the moved-here file. */
+  SVN_ERR(svn_wc__db_read_pristine_info(NULL, &kind, NULL, NULL, NULL, NULL,
+                                        &moved_away_checksum, NULL, NULL,
+                                        db, src_abspath, scratch_pool,
+                                        scratch_pool));
+  SVN_ERR(svn_wc__db_pristine_read(&post_update_contents, NULL, db,
+                                   src_abspath, moved_away_checksum,
+                                   scratch_pool, scratch_pool));
+  dst_relpath = svn_dirent_skip_ancestor(move_dst_op_root_abspath,
+                                         moved_to_abspath);
+  SVN_ERR(svn_editor_alter_file(tc_editor, dst_relpath, moved_here_revision,
+                                NULL, /* ### TODO props */
+                                moved_away_checksum,
+                                post_update_contents));
+  SVN_ERR(svn_stream_close(post_update_contents));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+update_moved_away_dir(svn_editor_t *tc_editor,
+                      const char *src_abspath,
+                      const char *moved_to_abspath,
+                      const char *move_dst_op_root_abspath,
+                      svn_wc__db_t *db,
+                      apr_pool_t *result_pool,
+                      apr_pool_t *scratch_pool)
+{
+  /* ### notify */
+
+  /* ### update prop content if changed */
+
+  /* ### update list of children if changed */
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+update_moved_away_subtree(svn_editor_t *tc_editor,
+                          const char *src_abspath,
+                          const char *moved_to_abspath,
+                          const char *move_dst_op_root_abspath,
+                          svn_wc__db_t *db,
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool)
+{
+  const apr_array_header_t *children;
+  apr_pool_t *iterpool;
+  int i;
+
+  SVN_ERR(update_moved_away_dir(tc_editor, src_abspath, moved_to_abspath,
+                                move_dst_op_root_abspath, db, result_pool,
+                                scratch_pool));
+
+  SVN_ERR(svn_wc__db_base_get_children(&children, db, src_abspath,
+                                       scratch_pool, scratch_pool));
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < children->nelts; i++)
+    {
+      const char *child_abspath;
+      svn_kind_t child_kind;
+      const char *child_dst_op_root_abspath;
+      const char *child_moved_to_abspath;
+
+      svn_pool_clear(iterpool);
+
+      child_abspath = svn_dirent_join(src_abspath,
+                                      APR_ARRAY_IDX(children, i, const char *),
+                                      iterpool);
+
+      /* Is this child part of our move operation? */
+      SVN_ERR(svn_wc__db_scan_deletion(NULL, &child_moved_to_abspath,
+                                       NULL, &child_dst_op_root_abspath,
+                                       db, child_abspath,
+                                       iterpool, iterpool));
+      if (child_dst_op_root_abspath == NULL ||
+          strcmp(child_dst_op_root_abspath, move_dst_op_root_abspath) != 0)
+        continue;
+
+      SVN_ERR(svn_wc__db_base_get_info(NULL, &child_kind, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, db,
+                                       child_abspath, iterpool, iterpool));
+
+      if (child_kind == svn_kind_file || child_kind == svn_kind_symlink)
+        SVN_ERR(update_moved_away_file(tc_editor, child_abspath,
+                                       child_moved_to_abspath,
+                                       move_dst_op_root_abspath,
+                                       db, result_pool, iterpool));
+      else if (child_kind == svn_kind_dir)
+        SVN_ERR(update_moved_away_subtree(tc_editor, child_abspath,
+                                          child_moved_to_abspath,
+                                          move_dst_op_root_abspath,
+                                          db, result_pool, iterpool));
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+drive_tree_conflict_editor(svn_editor_t *tc_editor,
+                           const char *src_abspath,
+                           const char *dst_abspath,
+                           svn_wc_operation_t operation,
+                           svn_wc_conflict_reason_t local_change,
+                           svn_wc_conflict_action_t incoming_change,
+                           svn_wc_conflict_version_t *old_version,
+                           svn_wc_conflict_version_t *new_version,
+                           svn_wc__db_t *db,
+                           svn_cancel_func_t cancel_func,
+                           void *cancel_baton,
+                           apr_pool_t *scratch_pool)
+{
+  /* 
+   * Refuse to auto-resolve unsupported tree conflicts.
+   */
+  /* ### Only handle conflicts created by update/switch operations for now. */
+  if (operation != svn_wc_operation_update &&
+      operation != svn_wc_operation_switch)
+    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                            _("Cannot auto-resolve tree-conflict on '%s'"),
+                            svn_dirent_local_style(src_abspath,
+                                                   scratch_pool));
+
+  /* 
+   * Drive the TC editor to transfer incoming changes from the move source
+   * to the move destination.
+   *
+   * The pre-update tree is within dst at the op-depth of the move's op-root.
+   * The post-update base tree is within src at op-depth zero.
+   *
+   * We walk the move source (i.e. the post-update tree), comparing each node 
+   * with the equivalent node at the move destination and applying the update
+   * to nodes at the move destination.
+   */
+  if (old_version->node_kind == svn_node_file)
+    SVN_ERR(update_moved_away_file(tc_editor, src_abspath, dst_abspath,
+                                   dst_abspath, db, scratch_pool,
+                                   scratch_pool));
+  else if (old_version->node_kind == svn_node_dir)
+    SVN_ERR(update_moved_away_subtree(tc_editor, src_abspath, dst_abspath,
+                                      dst_abspath, db, scratch_pool,
+                                      scratch_pool));
+
+  SVN_ERR(svn_editor_complete(tc_editor));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__update_moved_away_conflict_victim(svn_skel_t **work_items,
+                                          const char *victim_abspath,
+                                          svn_wc__db_t *db,
+                                          svn_cancel_func_t cancel_func,
+                                          void *cancel_baton,
+                                          apr_pool_t *scratch_pool)
+{
+  svn_editor_t *tc_editor;
+  struct tc_editor_baton *b;
+  svn_wc_operation_t operation;
+  svn_wc_conflict_reason_t local_change;
+  svn_wc_conflict_action_t incoming_change;
+
+  /* ### assumes wc write lock already held */
+
+  /* ### Open wc.db transaction. */
+
+  /* Construct editor baton. */
+  b = apr_pcalloc(scratch_pool, sizeof(*b));
+  b->src_abspath = victim_abspath;
+  SVN_ERR(svn_wc__db_scan_deletion(NULL, &b->dst_abspath, NULL, NULL,
+                                   db, victim_abspath, scratch_pool,
+                                   scratch_pool));
+  if (b->dst_abspath == NULL)
+    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                             _("The node '%s' has not been moved away"),
+                             svn_dirent_local_style(victim_abspath,
+                                                    scratch_pool));
+
+  SVN_ERR(get_tc_info(&operation, &local_change, &incoming_change,
+                      &b->old_version, &b->new_version,
+                      b->src_abspath, b->dst_abspath,
+                      db, scratch_pool, scratch_pool));
+  b->db = db;
+  b->work_items = work_items;
+
+  /* Create the editor... */
+  SVN_ERR(svn_editor_create(&tc_editor, b, cancel_func, cancel_baton,
+                            scratch_pool, scratch_pool));
+  SVN_ERR(svn_editor_setcb_many(tc_editor, &editor_ops, scratch_pool));
+
+  /* ... and drive it. */
+  SVN_ERR(drive_tree_conflict_editor(tc_editor, b->src_abspath,
+                                     b->dst_abspath, operation,
+                                     local_change, incoming_change,
+                                     b->old_version, b->new_version,
+                                     db, cancel_func, cancel_baton,
+                                     scratch_pool));
+
+  /* ### Close wc.db transaction. */
+
+  return SVN_NO_ERROR;
+}

Propchange: subversion/trunk/subversion/libsvn_wc/tree_conflict_editor.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/trunk/subversion/svn/conflict-callbacks.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?rev=1399679&r1=1399678&r2=1399679&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/conflict-callbacks.c (original)
+++ subversion/trunk/subversion/svn/conflict-callbacks.c Thu Oct 18 14:38:57 2012
@@ -856,7 +856,9 @@ svn_cl__conflict_func_interactive(svn_wc
                                                      scratch_pool),
                    readable_desc));
 
-      prompt = _("Select: (p) postpone, (r) mark-resolved, (h) help: ");
+      prompt = _("Select: (p) postpone, (r) mark-resolved, "
+                 "(mc) mine-conflict,\n"
+                 "        (tc) theirs-conflict, (h) help: ");
 
       while (1)
         {
@@ -867,8 +869,10 @@ svn_cl__conflict_func_interactive(svn_wc
           if (strcmp(answer, "h") == 0 || strcmp(answer, "?") == 0)
             {
               SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
-              _("  (p) postpone      - resolve the conflict later\n"
-                "  (r) resolved      - accept current working tree\n")));
+              _("  (p) postpone         - resolve the conflict later\n"
+                "  (r) resolved         - accept current working copy state\n"
+                "  (mc) mine-conflict   - prefer local change\n"
+                "  (tc) theirs-conflict - prefer incoming change\n")));
             }
           if (strcmp(answer, "p") == 0 || strcmp(answer, ":-p") == 0)
             {
@@ -880,6 +884,16 @@ svn_cl__conflict_func_interactive(svn_wc
               (*result)->choice = svn_wc_conflict_choose_merged;
               break;
             }
+          else if (strcmp(answer, "mc") == 0)
+            {
+              (*result)->choice = svn_wc_conflict_choose_mine_conflict;
+              break;
+            }
+          else if (strcmp(answer, "tc") == 0)
+            {
+              (*result)->choice = svn_wc_conflict_choose_theirs_conflict;
+              break;
+            }
         }
     }