You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ph...@apache.org on 2012/12/05 16:26:00 UTC

svn commit: r1417478 - in /subversion/trunk/subversion: libsvn_wc/wc-queries.sql libsvn_wc/wc_db.c libsvn_wc/wc_db_private.h libsvn_wc/wc_db_update_move.c tests/libsvn_wc/op-depth-test.c

Author: philip
Date: Wed Dec  5 15:25:56 2012
New Revision: 1417478

URL: http://svn.apache.org/viewvc?rev=1417478&view=rev
Log:
Update the move destination NODES rows when resolving a tree conflict
by following a move.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_DELETE_WORKING_OP_DEPTH, STMT_SELECT_LOCAL_RELPATH_OP_DEPTH,
   STMT_COPY_NODE_MOVE): New.
  (STMT_SELECT_OP_DEPTH_CHILDREN): Retrieve kind.

* subversion/libsvn_wc/wc_db_private.h
* subversion/libsvn_wc/wc_db.c
  (svn_wc__db_get_children_op_depth): New.

* subversion/libsvn_wc/wc_db_update_move.c
  (struct tc_editor_baton): Add source op-depth.
  (tc_editor_add_file): Allow it to be called.
  (tc_editor_delete): Delete NODES rows.
  (update_moved_away_file)): Add add/update parameter.
  (update_moved_away_subtree): Add op-depth parameter, rewrite to
   add/delete/modify children.
  (replace_moved_layer): New.
  (drive_tree_conflict_editor): Update the NODES rows in destination.
  (update_moved_away_conflict_victim): Get source op-depth.

* subversion/tests/libsvn_wc/op-depth-test.c
  (move_update): Extend, remove XFAIL.

Modified:
    subversion/trunk/subversion/libsvn_wc/wc-queries.sql
    subversion/trunk/subversion/libsvn_wc/wc_db.c
    subversion/trunk/subversion/libsvn_wc/wc_db_private.h
    subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c
    subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c

Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1417478&r1=1417477&r2=1417478&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Wed Dec  5 15:25:56 2012
@@ -220,8 +220,36 @@ DELETE FROM nodes
 WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
   AND op_depth = 0
 
+-- STMT_DELETE_WORKING_OP_DEPTH
+DELETE FROM nodes
+WHERE wc_id = ?1 
+  AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
+  AND op_depth = ?3
+
+-- STMT_SELECT_LOCAL_RELPATH_OP_DEPTH
+SELECT local_relpath
+FROM nodes
+WHERE wc_id = ?1
+  AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
+  AND op_depth = ?3
+
+-- STMT_COPY_NODE_MOVE
+INSERT OR REPLACE INTO nodes (
+    wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path,
+    revision, presence, depth, kind, changed_revision, changed_date,
+    changed_author, checksum, properties, translated_size, last_mod_time,
+    symlink_target, moved_here )
+SELECT
+    wc_id, ?4 /*local_relpath */, ?5 /*op_depth*/, ?6 /* parent_relpath */,
+    repos_id,
+    repos_path, revision, presence, depth, kind, changed_revision,
+    changed_date, changed_author, checksum, properties, translated_size,
+    last_mod_time, symlink_target, 1
+FROM nodes
+WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
+
 -- STMT_SELECT_OP_DEPTH_CHILDREN
-SELECT local_relpath FROM nodes
+SELECT local_relpath, kind FROM nodes
 WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = ?3
   AND (?3 != 0 OR file_external is NULL)
 

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1417478&r1=1417477&r2=1417478&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Wed Dec  5 15:25:56 2012
@@ -1237,6 +1237,41 @@ gather_repo_children(const apr_array_hea
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_wc__db_get_children_op_depth(apr_hash_t **children,
+                                 svn_wc__db_wcroot_t *wcroot,
+                                 const char *local_relpath,
+                                 int op_depth,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+
+  *children = apr_hash_make(result_pool);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_OP_DEPTH_CHILDREN));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                            op_depth));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  while (have_row)
+    {
+      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      svn_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_kind_t));
+
+      *child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
+      apr_hash_set(*children,
+                   svn_relpath_basename(child_relpath, result_pool),
+                   APR_HASH_KEY_STRING, child_kind);
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
+
 
 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
  * Else, return FALSE. */

Modified: subversion/trunk/subversion/libsvn_wc/wc_db_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_private.h?rev=1417478&r1=1417477&r2=1417478&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_private.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_private.h Wed Dec  5 15:25:56 2012
@@ -323,4 +323,13 @@ svn_wc__db_with_txn(svn_wc__db_wcroot_t 
                     apr_pool_t *scratch_pool);
 
 
+/* Return CHILDREN mapping const char * names to svn_kind_t * for the
+   children of LOCAL_RELPATH at OP_DEPTH. */
+svn_error_t *
+svn_wc__db_get_children_op_depth(apr_hash_t **children,
+                                 svn_wc__db_wcroot_t *wcroot,
+                                 const char *local_relpath,
+                                 int op_depth,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool);
 #endif /* WC_DB_PRIVATE_H */

Modified: subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c?rev=1417478&r1=1417477&r2=1417478&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c Wed Dec  5 15:25:56 2012
@@ -64,6 +64,7 @@ struct tc_editor_baton {
   svn_wc__db_wcroot_t *wcroot;
   const char *move_root_src_relpath;
   const char *move_root_dst_relpath;
+  int src_op_depth;
   svn_wc_conflict_version_t *old_version;
   svn_wc_conflict_version_t *new_version;
   svn_wc_notify_func2_t notify_func;
@@ -91,7 +92,10 @@ tc_editor_add_file(void *baton,
                    svn_revnum_t replaces_rev,
                    apr_pool_t *scratch_pool)
 {
-  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+  /* ### TODO check for, and flag, tree conflict */
+
+  /* ### TODO Extend base-delete. */
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *
@@ -351,7 +355,21 @@ tc_editor_delete(void *baton,
                  svn_revnum_t revision,
                  apr_pool_t *scratch_pool)
 {
-  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
+  struct tc_editor_baton *b = baton;
+  svn_sqlite__stmt_t *stmt;
+
+  /* Deleting the ROWS is valid so long as we update the parent before
+     committing the transaction. */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb,
+                                    STMT_DELETE_WORKING_OP_DEPTH));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath,
+                            relpath_depth(b->move_root_dst_relpath)));
+  SVN_ERR(svn_sqlite__step_done(stmt));
+
+  /* ### TODO Retract base-delete. */
+
+  /* ### TODO check for, and flag, tree conflict */
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *
@@ -541,6 +559,7 @@ get_tc_info(svn_wc_operation_t *operatio
  */
 static svn_error_t *
 update_moved_away_file(svn_editor_t *tc_editor,
+                       svn_boolean_t add,
                        const char *src_relpath,
                        const char *dst_relpath,
                        const char *move_root_dst_relpath,
@@ -552,7 +571,7 @@ update_moved_away_file(svn_editor_t *tc_
   svn_kind_t kind;
   svn_stream_t *post_update_contents;
   const svn_checksum_t *move_src_checksum;
-                    
+
   /* 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,
@@ -564,11 +583,20 @@ update_moved_away_file(svn_editor_t *tc_
   SVN_ERR(svn_wc__db_pristine_read(&post_update_contents, NULL, db,
                                    wcroot->abspath, move_src_checksum,
                                    scratch_pool, scratch_pool));
-  SVN_ERR(svn_editor_alter_file(tc_editor, dst_relpath,
-                                move_root_dst_revision,
-                                NULL, /* ### TODO props */
-                                move_src_checksum,
-                                post_update_contents));
+                    
+  if (add)
+    /* FIXME: editor API violation: missing svn_editor_alter_directory. */
+    SVN_ERR(svn_editor_add_file(tc_editor, dst_relpath,
+                                move_src_checksum, post_update_contents,
+                                apr_hash_make(scratch_pool), /* ### TODO props */
+                                move_root_dst_revision));
+  else
+    SVN_ERR(svn_editor_alter_file(tc_editor, dst_relpath,
+                                  move_root_dst_revision,
+                                  NULL, /* ### TODO props */
+                                  move_src_checksum,
+                                  post_update_contents));
+
   SVN_ERR(svn_stream_close(post_update_contents));
 
   return SVN_NO_ERROR;
@@ -601,77 +629,153 @@ static svn_error_t *
 update_moved_away_subtree(svn_editor_t *tc_editor,
                           const char *src_relpath,
                           const char *dst_relpath,
+                          int src_op_depth,
                           const char *move_root_dst_relpath,
                           svn_revnum_t move_root_dst_revision,
                           svn_wc__db_t *db,
                           svn_wc__db_wcroot_t *wcroot,
                           apr_pool_t *scratch_pool)
 {
-  const apr_array_header_t *children;
+  apr_hash_t *src_children, *dst_children;
   apr_pool_t *iterpool;
-  int i;
+  apr_hash_index_t *hi;
 
   SVN_ERR(update_moved_away_dir(tc_editor, src_relpath, dst_relpath,
                                 move_root_dst_relpath,
                                 move_root_dst_revision,
                                 db, wcroot, scratch_pool));
 
-  SVN_ERR(svn_wc__db_base_get_children(&children, db,
-                                       svn_dirent_join(wcroot->abspath,
-                                                       src_relpath,
-                                                       scratch_pool),
-                                       scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__db_get_children_op_depth(&src_children, wcroot,
+                                           src_relpath, src_op_depth,
+                                           scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__db_get_children_op_depth(&dst_children, wcroot,
+                                           dst_relpath,
+                                           relpath_depth(move_root_dst_relpath),
+                                           scratch_pool, scratch_pool));
   iterpool = svn_pool_create(scratch_pool);
-  for (i = 0; i < children->nelts; i++)
+  for (hi = apr_hash_first(scratch_pool, src_children);
+       hi;
+       hi = apr_hash_next(hi))
     {
-      const char *child_src_relpath;
-      svn_kind_t child_kind;
-      const char *child_dst_op_root_relpath;
+      const char *src_name = svn__apr_hash_index_key(hi);
+      svn_kind_t *src_kind = svn__apr_hash_index_val(hi);
+      svn_kind_t *dst_kind = apr_hash_get(dst_children, src_name,
+                                          APR_HASH_KEY_STRING);
+      svn_boolean_t is_add = FALSE;
+      const char *child_src_relpath, *child_dst_relpath;
+
+      svn_pool_clear(iterpool);
+
+      child_src_relpath = svn_relpath_join(src_relpath, src_name, iterpool);
+      child_dst_relpath = svn_relpath_join(dst_relpath, src_name, iterpool);
+
+      if (!dst_kind || (*src_kind != *dst_kind))
+        {
+          is_add = TRUE;
+          if (dst_kind)
+            /* FIXME:editor API violation:missing svn_editor_alter_directory. */
+            SVN_ERR(svn_editor_delete(tc_editor, child_dst_relpath,
+                                      move_root_dst_revision));
+        }
+
+      if (*src_kind == svn_kind_file || *src_kind == svn_kind_symlink)
+        {
+          SVN_ERR(update_moved_away_file(tc_editor, is_add,
+                                         child_src_relpath,
+                                         child_dst_relpath,
+                                         move_root_dst_relpath,
+                                         move_root_dst_revision,
+                                         db, wcroot, iterpool));
+        }
+      else if (*src_kind == svn_kind_dir)
+        {
+          SVN_ERR(update_moved_away_subtree(tc_editor,
+                                            child_src_relpath,
+                                            child_dst_relpath,
+                                            src_op_depth,
+                                            move_root_dst_relpath,
+                                            move_root_dst_revision,
+                                            db, wcroot, iterpool));
+        }
+      else
+        ; /* ### TODO */
+
+      apr_hash_set(dst_children, src_name, APR_HASH_KEY_STRING, NULL);
+    }
+  for (hi = apr_hash_first(scratch_pool, dst_children);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *dst_name = svn__apr_hash_index_key(hi);
       const char *child_dst_relpath;
 
       svn_pool_clear(iterpool);
+      child_dst_relpath = svn_relpath_join(dst_relpath, dst_name, iterpool);
 
-      child_src_relpath = svn_relpath_join(src_relpath,
-                                           APR_ARRAY_IDX(children, i,
-                                                         const char *),
-                                           iterpool);
-
-      /* Is this child part of our move operation? */
-      /* ### need to handle children which were newly added or removed
-       * ### during the update */
-      SVN_ERR(svn_wc__db_scan_deletion_internal(NULL, &child_dst_relpath, NULL,
-                                                &child_dst_op_root_relpath,
-                                                wcroot, child_src_relpath,
-                                                iterpool, iterpool));
-      if (child_dst_op_root_relpath == NULL ||
-          strcmp(child_dst_op_root_relpath, move_root_dst_relpath) != 0)
-        continue;
-
-      SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind,
-                                                NULL, NULL, NULL, NULL,
-                                                NULL, NULL, NULL, NULL,
-                                                NULL, NULL, NULL, NULL,
-                                                wcroot, child_src_relpath,
-                                                iterpool, iterpool));
-
-      if (child_kind == svn_kind_file || child_kind == svn_kind_symlink)
-        SVN_ERR(update_moved_away_file(tc_editor, child_src_relpath,
-                                       child_dst_relpath,
-                                       move_root_dst_relpath,
-                                       move_root_dst_revision,
-                                       db, wcroot, iterpool));
-      else if (child_kind == svn_kind_dir)
-        SVN_ERR(update_moved_away_subtree(tc_editor, child_src_relpath,
-                                          child_dst_relpath,
-                                          move_root_dst_relpath,
-                                          move_root_dst_revision,
-                                          db, wcroot, iterpool));
+      /* FIXME: editor API violation: missing svn_editor_alter_directory. */
+      SVN_ERR(svn_editor_delete(tc_editor, child_dst_relpath,
+                                move_root_dst_revision));
     }
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
 
+/* Update the single op-depth layer in the move destination subtree
+   rooted at DST_RELPATH to make it match the move source subtree
+   rooted at SRC_RELPATH. */
+static svn_error_t *
+replace_moved_layer(const char *src_relpath,
+                    const char *dst_relpath,
+                    int src_op_depth,
+                    svn_wc__db_t *db,
+                    svn_wc__db_wcroot_t *wcroot,
+                    apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  int dst_op_depth = relpath_depth(dst_relpath);
+
+  /* Replace entire subtree at one op-depth. */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_LOCAL_RELPATH_OP_DEPTH));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
+                            src_relpath, src_op_depth));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  while (have_row)
+    {
+      svn_error_t *err;
+      svn_sqlite__stmt_t *stmt2;
+      const char *src_cp_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      const char *dst_cp_relpath
+        = svn_relpath_join(dst_relpath,
+                           svn_relpath_skip_ancestor(src_relpath,
+                                                     src_cp_relpath),
+                           scratch_pool);
+
+      err = svn_sqlite__get_statement(&stmt2, wcroot->sdb,
+                                      STMT_COPY_NODE_MOVE);
+      if (!err)
+        err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id,
+                                src_cp_relpath, src_op_depth,
+                                dst_cp_relpath, dst_op_depth,
+                                svn_relpath_dirname(dst_cp_relpath,
+                                                    scratch_pool));
+      if (!err)
+        err = svn_sqlite__step_done(stmt2);
+      if (err)
+        return svn_error_compose_create(err, svn_sqlite__reset(stmt));
+ 
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  /* TODO: extend/retract any base-deleted layers to account for
+     added/removed nodes in the replaced layer. */
+
+  return SVN_NO_ERROR;
+}
+
 /* Transfer changes from the move source to the move destination.
  *
  * Drive the editor TC_EDITOR with the difference between DST_RELPATH
@@ -687,6 +791,7 @@ static svn_error_t *
 drive_tree_conflict_editor(svn_editor_t *tc_editor,
                            const char *src_relpath,
                            const char *dst_relpath,
+                           int src_op_depth,
                            svn_wc_operation_t operation,
                            svn_wc_conflict_reason_t local_change,
                            svn_wc_conflict_action_t incoming_change,
@@ -715,16 +820,20 @@ drive_tree_conflict_editor(svn_editor_t 
    * 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_relpath, dst_relpath,
+    SVN_ERR(update_moved_away_file(tc_editor, FALSE, src_relpath, dst_relpath,
                                    dst_relpath, old_version->peg_rev,
                                    db, wcroot, scratch_pool));
   else if (old_version->node_kind == svn_node_dir)
     SVN_ERR(update_moved_away_subtree(tc_editor, src_relpath, dst_relpath,
+                                      src_op_depth,
                                       dst_relpath, old_version->peg_rev,
                                       db, wcroot, scratch_pool));
 
   SVN_ERR(svn_editor_complete(tc_editor));
 
+  SVN_ERR(replace_moved_layer(src_relpath, dst_relpath, src_op_depth,
+                              db, wcroot, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -754,6 +863,8 @@ update_moved_away_conflict_victim(void *
   struct update_moved_away_conflict_victim_baton *b = baton;
   svn_editor_t *tc_editor;
   struct tc_editor_baton *tc_editor_baton;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
 
   /* ### assumes wc write lock already held */
 
@@ -779,6 +890,23 @@ update_moved_away_conflict_victim(void *
   tc_editor_baton->notify_baton = b->notify_baton;
   tc_editor_baton->result_pool = b->result_pool;
 
+  /* ### TODO get from svn_wc__db_scan_deletion_internal? */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_NODE_INFO));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, victim_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  if (have_row)
+    SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  if (have_row)
+    tc_editor_baton->src_op_depth = svn_sqlite__column_int(stmt, 0);
+  SVN_ERR(svn_sqlite__reset(stmt));
+  if (!have_row)
+    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                             _("'%s' is not deleted"),
+                             svn_dirent_local_style(
+                               svn_dirent_join(wcroot->abspath, victim_relpath,
+                                               scratch_pool),
+                               scratch_pool));
   /* Create the editor... */
   SVN_ERR(svn_editor_create(&tc_editor, tc_editor_baton,
                             b->cancel_func, b->cancel_baton,
@@ -789,6 +917,7 @@ update_moved_away_conflict_victim(void *
   SVN_ERR(drive_tree_conflict_editor(tc_editor,
                                      tc_editor_baton->move_root_src_relpath,
                                      tc_editor_baton->move_root_dst_relpath,
+                                     tc_editor_baton->src_op_depth,
                                      b->operation,
                                      b->local_change, b->incoming_change,
                                      tc_editor_baton->old_version,

Modified: subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c?rev=1417478&r1=1417477&r2=1417478&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c Wed Dec  5 15:25:56 2012
@@ -4294,12 +4294,15 @@ move_update(const svn_test_opts_t *opts,
   SVN_ERR(sbox_wc_mkdir(&b, "A"));
   SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
   sbox_file_write(&b, "A/B/f", "r1 content\n");
+  sbox_file_write(&b, "A/B/h", "r1 content\n");
   SVN_ERR(sbox_wc_add(&b, "A/B/f"));
+  SVN_ERR(sbox_wc_add(&b, "A/B/h"));
   SVN_ERR(sbox_wc_commit(&b, ""));
   sbox_file_write(&b, "A/B/f", "r1 content\nr2 content\n");
   SVN_ERR(sbox_wc_commit(&b, ""));
   sbox_file_write(&b, "A/B/g", "r3 content\n");
   SVN_ERR(sbox_wc_add(&b, "A/B/g"));
+  SVN_ERR(sbox_wc_delete(&b, "A/B/h"));
   SVN_ERR(sbox_wc_commit(&b, ""));
   SVN_ERR(sbox_wc_update(&b, "", 1));
 
@@ -4311,12 +4314,15 @@ move_update(const svn_test_opts_t *opts,
       {0, "A",        "normal",       1, "A"},
       {0, "A/B",      "normal",       1, "A/B"},
       {0, "A/B/f",    "normal",       1, "A/B/f"},
+      {0, "A/B/h",    "normal",       1, "A/B/h"},
       {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",      "base-deleted", NO_COPY_FROM},
       {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
+      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
       {1, "A2",       "normal",       1, "A", MOVED_HERE},
       {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
       {1, "A2/B/f",   "normal",       1, "A/B/f", MOVED_HERE},
+      {1, "A2/B/h",   "normal",       1, "A/B/h", MOVED_HERE},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
@@ -4330,12 +4336,15 @@ move_update(const svn_test_opts_t *opts,
       {0, "A",        "normal",       2, "A"},
       {0, "A/B",      "normal",       2, "A/B"},
       {0, "A/B/f",    "normal",       2, "A/B/f"},
+      {0, "A/B/h",    "normal",       2, "A/B/h"},
       {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",      "base-deleted", NO_COPY_FROM},
       {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
+      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
       {1, "A2",       "normal",       1, "A", MOVED_HERE},
       {1, "A2/B",     "normal",       1, "A/B", MOVED_HERE},
       {1, "A2/B/f",   "normal",       1, "A/B/f", MOVED_HERE},
+      {1, "A2/B/h",   "normal",       1, "A/B/h", MOVED_HERE},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
@@ -4349,12 +4358,15 @@ move_update(const svn_test_opts_t *opts,
       {0, "A",        "normal",       2, "A"},
       {0, "A/B",      "normal",       2, "A/B"},
       {0, "A/B/f",    "normal",       2, "A/B/f"},
+      {0, "A/B/h",    "normal",       2, "A/B/h"},
       {1, "A",        "base-deleted", NO_COPY_FROM, "A2"},
       {1, "A/B",      "base-deleted", NO_COPY_FROM},
       {1, "A/B/f",    "base-deleted", NO_COPY_FROM},
+      {1, "A/B/h",    "base-deleted", NO_COPY_FROM},
       {1, "A2",       "normal",       2, "A", MOVED_HERE},
       {1, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
       {1, "A2/B/f",   "normal",       2, "A/B/f", MOVED_HERE},
+      {1, "A2/B/h",   "normal",       2, "A/B/h", MOVED_HERE},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
@@ -4376,12 +4388,12 @@ move_update(const svn_test_opts_t *opts,
       {1, "A2",       "normal",       2, "A", MOVED_HERE},
       {1, "A2/B",     "normal",       2, "A/B", MOVED_HERE},
       {1, "A2/B/f",   "normal",       2, "A/B/f", MOVED_HERE},
+      {1, "A2/B/h",   "normal",       2, "A/B/h", MOVED_HERE},
       {0}
     };
     SVN_ERR(check_db_rows(&b, "", nodes));
   }
 
-  /* Are we going to handle this case? */
   SVN_ERR(sbox_wc_resolve(&b, "A"));
   {
     nodes_row_t nodes[] = {
@@ -4894,7 +4906,7 @@ struct svn_test_descriptor_t test_funcs[
                        "move_on_move2"),
     SVN_TEST_OPTS_PASS(move_added,
                        "move_added"),
-    SVN_TEST_OPTS_XFAIL(move_update,
+    SVN_TEST_OPTS_PASS(move_update,
                        "move_update"),
     SVN_TEST_OPTS_PASS(test_scan_delete,
                        "scan_delete"),