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 2011/07/30 11:22:15 UTC

svn commit: r1152436 - in /subversion/trunk/subversion: libsvn_wc/entries.c libsvn_wc/info.c libsvn_wc/node.c libsvn_wc/wc-queries.sql libsvn_wc/wc_db.c libsvn_wc/wc_db.h tests/libsvn_wc/db-test.c

Author: stsp
Date: Sat Jul 30 09:22:14 2011
New Revision: 1152436

URL: http://svn.apache.org/viewvc?rev=1152436&view=rev
Log:
Fixed version of the change originally committed in r1152345 and
reverted again in r1152348:

Extend svn_wc__db_scan_deletion() to return the moved-to path of
of a moved node, if requested, in MOVED_TO_ABSPATH.

This function previously returned the op-root of the copied-half of the
move as MOVED_TO_ABSPATH, but (luckily) none of the callers requested it.
Return the op-root in a new output parameter COPY_OP_ROOT_ABSPATH instead.

* subversion/libsvn_wc/wc_db.c
  (scan_deletion_baton_t): Add COPY_OP_ROOT_RELPATH.
  (scan_deletion_txn): If the node has been moved-away, compute
   and return the moved-to relpath as well as the the op-root of
   the copied-half of the move.
   Also, do not assert that every node being moved has a BASE.
   This isn't true for sequences like: mv A B; mv B/f B/e;
   In the second move, B/f has no BASE because it is part of the
   copied-half B of the first move.
  (scan_deletion): Add new output parameter COPY_OP_ROOT_RELPATH and
   pass it into the scan_deletion baton.
  (get_info_for_copy, read_url_txn, svn_wc__db_global_relocate): Update
   scan_deletion() callers, passing NULL for COPY_OP_ROOT_RELPATH.
  (svn_wc__db_scan_deletion): Add new output parameter COPY_OP_ROOT_ABSPATH.
   Convert relative paths in moved-to information provided by scan_deletion()
   into absolute paths, and return them.

* subversion/libsvn_wc/wc_db.h
  (svn_wc__db_scan_deletion): Add COPY_OP_ROOT_ABSPATH parameter and update
   the docstring.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_SELECT_DELETION_INFO): Get moved-to information from the BASE since
   it is stored there as of r1152410.

* subversion/libsvn_wc/node.c
  (svn_wc__internal_get_repos_info, svn_wc__node_get_deleted_ancestor,
   svn_wc__internal_get_commit_base_rev, svn_wc__internal_node_get_schedule):
    Update callers, passing NULL for COPY_OP_ROOT_ABSPATH.

* subversion/libsvn_wc/entries.c
  (get_info_for_deleted, read_one_entry): Update callers, passing NULL
   for COPY_OP_ROOT_ABSPATH.

* subversion/libsvn_wc/info.c
  (build_info_for_node): Update caller, passing NULL for COPY_OP_ROOT_ABSPATH.

* subversion/tests/libsvn_wc/db-test.c
  (TESTING_DATA): Put moved-to data into BASE layer. Add some missing
   move-target nodes that are needed by scan_deletion() to properly resolve
   moved-to information.
  (test_scan_deletion): Update callers, verify COPY_OP_ROOT_ABSPATH where
   a move is being resolved, else pass NULL for COPY_OP_ROOT_ABSPATH.

Modified:
    subversion/trunk/subversion/libsvn_wc/entries.c
    subversion/trunk/subversion/libsvn_wc/info.c
    subversion/trunk/subversion/libsvn_wc/node.c
    subversion/trunk/subversion/libsvn_wc/wc-queries.sql
    subversion/trunk/subversion/libsvn_wc/wc_db.c
    subversion/trunk/subversion/libsvn_wc/wc_db.h
    subversion/trunk/subversion/tests/libsvn_wc/db-test.c

Modified: subversion/trunk/subversion/libsvn_wc/entries.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/entries.c?rev=1152436&r1=1152435&r2=1152436&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/entries.c (original)
+++ subversion/trunk/subversion/libsvn_wc/entries.c Sat Jul 30 09:22:14 2011
@@ -272,7 +272,7 @@ get_info_for_deleted(svn_wc_entry_t *ent
 
      SVN_ERR(svn_wc__db_scan_deletion(NULL,
                                       NULL,
-                                      &work_del_abspath,
+                                      &work_del_abspath, NULL,
                                       db, entry_abspath,
                                       scratch_pool, scratch_pool));
 
@@ -525,7 +525,7 @@ read_one_entry(const svn_wc_entry_t **ne
         {
           const char *work_del_abspath;
           SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
-                                           &work_del_abspath,
+                                           &work_del_abspath, NULL,
                                            db, entry_abspath,
                                            scratch_pool, scratch_pool));
 

Modified: subversion/trunk/subversion/libsvn_wc/info.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/info.c?rev=1152436&r1=1152435&r2=1152436&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/info.c (original)
+++ subversion/trunk/subversion/libsvn_wc/info.c Sat Jul 30 09:22:14 2011
@@ -200,7 +200,7 @@ build_info_for_node(svn_wc__info2_t **in
 
       /* And now fetch the url and revision of what will be deleted */
       SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
-                                       &work_del_abspath,
+                                       &work_del_abspath, NULL,
                                        db, local_abspath,
                                        scratch_pool, scratch_pool));
       if (work_del_abspath != NULL)

Modified: subversion/trunk/subversion/libsvn_wc/node.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/node.c?rev=1152436&r1=1152435&r2=1152436&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/node.c (original)
+++ subversion/trunk/subversion/libsvn_wc/node.c Sat Jul 30 09:22:14 2011
@@ -182,7 +182,7 @@ svn_wc__internal_get_repos_info(const ch
       const char *base_del_abspath, *wrk_del_abspath;
 
       SVN_ERR(svn_wc__db_scan_deletion(&base_del_abspath, NULL,
-                                       &wrk_del_abspath,
+                                       &wrk_del_abspath, NULL,
                                        db, local_abspath,
                                        scratch_pool, scratch_pool));
 
@@ -768,7 +768,7 @@ svn_wc__node_get_deleted_ancestor(const 
 
   if (status == svn_wc__db_status_deleted)
     SVN_ERR(svn_wc__db_scan_deletion(deleted_ancestor_abspath, NULL, NULL,
-                                     wc_ctx->db, local_abspath,
+                                     NULL, wc_ctx->db, local_abspath,
                                      result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
@@ -1017,7 +1017,7 @@ svn_wc__internal_get_commit_base_rev(svn
       const char *work_del_abspath;
 
       SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
-                                       &work_del_abspath,
+                                       &work_del_abspath, NULL,
                                        db, local_abspath,
                                        scratch_pool, scratch_pool));
       if (work_del_abspath != NULL)
@@ -1160,7 +1160,7 @@ svn_wc__internal_node_get_schedule(svn_w
 
               /* Find out details of our deletion.  */
               SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
-                                               &work_del_abspath,
+                                               &work_del_abspath, NULL,
                                                db, local_abspath,
                                                scratch_pool, scratch_pool));
 

Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1152436&r1=1152435&r2=1152436&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Sat Jul 30 09:22:14 2011
@@ -278,7 +278,7 @@ SELECT dav_cache FROM nodes
 WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0
 
 -- STMT_SELECT_DELETION_INFO
-SELECT nodes_base.presence, nodes_work.presence, nodes_work.moved_to,
+SELECT nodes_base.presence, nodes_work.presence, nodes_base.moved_to,
        nodes_work.op_depth
 FROM nodes AS nodes_work
 LEFT OUTER JOIN nodes nodes_base ON nodes_base.wc_id = nodes_work.wc_id

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1152436&r1=1152435&r2=1152436&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Sat Jul 30 09:22:14 2011
@@ -377,6 +377,7 @@ static svn_error_t *
 scan_deletion(const char **base_del_relpath,
               const char **moved_to_relpath,
               const char **work_del_relpath,
+              const char **copy_op_root_relpath,
               svn_wc__db_wcroot_t *wcroot,
               const char *local_relpath,
               apr_pool_t *result_pool,
@@ -3361,7 +3362,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
       const char *base_del_relpath, *work_del_relpath;
 
       SVN_ERR(scan_deletion(&base_del_relpath, NULL, &work_del_relpath,
-                            wcroot, local_relpath, scratch_pool,
+                            NULL, wcroot, local_relpath, scratch_pool,
                             scratch_pool));
       if (work_del_relpath)
         {
@@ -7414,7 +7415,7 @@ read_url_txn(void *baton,
           const char *work_del_relpath;
 
           SVN_ERR(scan_deletion(&base_del_relpath, NULL, &work_del_relpath,
-                                wcroot, local_relpath,
+                                NULL, wcroot, local_relpath,
                                 scratch_pool, scratch_pool));
 
           if (base_del_relpath)
@@ -8170,7 +8171,7 @@ svn_wc__db_global_relocate(svn_wc__db_t 
       if (status == svn_wc__db_status_deleted)
         {
           const char *work_del_relpath;
-          SVN_ERR(scan_deletion(NULL, NULL, &work_del_relpath,
+          SVN_ERR(scan_deletion(NULL, NULL, &work_del_relpath, NULL,
                                 wcroot, local_dir_relpath,
                                 scratch_pool, scratch_pool));
           if (work_del_relpath)
@@ -9569,6 +9570,7 @@ struct scan_deletion_baton_t
   const char **base_del_relpath;
   const char **moved_to_relpath;
   const char **work_del_relpath;
+  const char **copy_op_root_relpath;
   apr_pool_t *result_pool;
 };
 
@@ -9584,7 +9586,6 @@ scan_deletion_txn(void *baton,
   const char *child_relpath = NULL;
   svn_wc__db_status_t child_presence;
   svn_boolean_t child_has_base = FALSE;
-  svn_boolean_t found_moved_to = FALSE;
   apr_int64_t local_op_depth, op_depth;
 
   /* Initialize all the OUT parameters.  */
@@ -9594,6 +9595,8 @@ scan_deletion_txn(void *baton,
     *sd_baton->moved_to_relpath = NULL;
   if (sd_baton->work_del_relpath != NULL)
     *sd_baton->work_del_relpath = NULL;
+  if (sd_baton->copy_op_root_relpath != NULL)
+    *sd_baton->copy_op_root_relpath = NULL;
 
   /* Initialize to something that won't denote an important parent/child
      transition.  */
@@ -9710,25 +9713,99 @@ scan_deletion_txn(void *baton,
              gimmick, not a real node that may have been deleted.  */
         }
 
-      /* Only grab the nearest ancestor.  */
-      if (!found_moved_to &&
-          (sd_baton->moved_to_relpath != NULL
+      if ((sd_baton->moved_to_relpath != NULL
+                || sd_baton->copy_op_root_relpath != NULL
                 || sd_baton->base_del_relpath != NULL)
           && !svn_sqlite__column_is_null(stmt, 2 /* moved_to */))
         {
-          /* There better be a BASE_NODE (that was moved-away).  */
-          SVN_ERR_ASSERT(have_base);
+          const char *copy_op_root_relpath;
+          const char *moved_to_relpath;
 
-          found_moved_to = TRUE;
+          copy_op_root_relpath = svn_sqlite__column_text(stmt, 2,
+                                                         scratch_pool);
+          if (current_relpath == local_relpath)
+            {
+              /* The starting node is the op_root of the delete-half of
+               * the move, so the op_root of the copied-half equals the
+               * moved-to relpath. Note that the moved-to relpath in the DB
+               * is kept up-to-date if a node is moved multiple times. */
+              if (sd_baton->moved_to_relpath)
+                *sd_baton->moved_to_relpath =
+                  apr_pstrdup(sd_baton->result_pool, copy_op_root_relpath);
+            }
+          else
+            {
+              const char *moved_child_relpath;
+              svn_wc__db_status_t moved_child_status;
+              svn_boolean_t found_child;
+              svn_error_t *err;
 
-          /* This makes things easy. It's the BASE_DEL_ABSPATH!  */
-          if (sd_baton->base_del_relpath != NULL)
-            *sd_baton->base_del_relpath = apr_pstrdup(sd_baton->result_pool,
-                                                      current_relpath);
+              /* The CURRENT_RELPATH is the op_root of the delete-half of
+               * the move. LOCAL_RELPATH is a child that was moved along.
+               * Compute the child's new location within the move target. */
+              moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
+                                                              local_relpath);
+              SVN_ERR_ASSERT(moved_child_relpath &&
+                             strlen(moved_child_relpath) > 0);
+              moved_to_relpath = svn_relpath_join(copy_op_root_relpath,
+                                                  moved_child_relpath,
+                                                  scratch_pool);
+              
+              /* Figure out what happened to the child after it was moved
+               * along. Maybe the child was moved-away further, either by
+               * itself, or along with some intermediate parent node.
+               *
+               * If the child was deleted instead of moved-away,
+               * the resulting MOVED_TO_RELPATH is NULL. */
+              err = read_info(&moved_child_status, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, NULL, wcroot,
+                              moved_to_relpath, scratch_pool, scratch_pool);
+              if (err)
+                {
+                  if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+                    {
+                      /* Tolerate missing children. A likely cause is that
+                       * the moved-to information in BASE is incorrect.
+                       * Just treat this as a normal deletion. */
+                      svn_error_clear(err); 
+                      moved_to_relpath = NULL;
+                      copy_op_root_relpath = NULL;
+                      found_child = FALSE;
+                    }
+                  else
+                    return svn_error_compose_create(err,
+                                                    svn_sqlite__reset(stmt));
+                }
+              else
+                found_child = TRUE;
+
+              if (found_child &&
+                  moved_child_status == svn_wc__db_status_deleted)
+                {
+                  err = scan_deletion(NULL, &moved_to_relpath, NULL,
+                                      NULL, wcroot, moved_to_relpath,
+                                      scratch_pool, scratch_pool);
+                  if (err)
+                   return svn_error_compose_create(err,
+                                                   svn_sqlite__reset(stmt));
+                }
+              if (sd_baton->moved_to_relpath)
+                *sd_baton->moved_to_relpath = moved_to_relpath ? 
+                  apr_pstrdup(sd_baton->result_pool, moved_to_relpath) : NULL;
+            }
 
-          if (sd_baton->moved_to_relpath != NULL)
-            *sd_baton->moved_to_relpath = apr_pstrdup(sd_baton->result_pool,
-                                    svn_sqlite__column_text(stmt, 2, NULL));
+          /* CURRENT_RELPATH is the op_root of the delete-half of the move,
+           * so it's the BASE_DEL_RELPATH. */
+          if (sd_baton->base_del_relpath)
+            *sd_baton->base_del_relpath =
+              apr_pstrdup(sd_baton->result_pool, current_relpath);
+
+          /* The copy_op_root_relpath is the moved-to relpath from the DB. */
+          if (sd_baton->copy_op_root_relpath)
+            *sd_baton->copy_op_root_relpath = copy_op_root_relpath ?
+              apr_pstrdup(sd_baton->result_pool, copy_op_root_relpath) : NULL;
         }
 
       op_depth = svn_sqlite__column_int64(stmt, 3);
@@ -9769,6 +9846,7 @@ static svn_error_t *
 scan_deletion(const char **base_del_relpath,
               const char **moved_to_relpath,
               const char **work_del_relpath,
+              const char **copy_op_root_relpath,
               svn_wc__db_wcroot_t *wcroot,
               const char *local_relpath,
               apr_pool_t *result_pool,
@@ -9777,8 +9855,9 @@ scan_deletion(const char **base_del_relp
   struct scan_deletion_baton_t sd_baton;
 
   sd_baton.base_del_relpath = base_del_relpath;
-  sd_baton.moved_to_relpath = moved_to_relpath;
   sd_baton.work_del_relpath = work_del_relpath;
+  sd_baton.moved_to_relpath = moved_to_relpath;
+  sd_baton.copy_op_root_relpath = copy_op_root_relpath;
   sd_baton.result_pool = result_pool;
 
   return svn_error_trace(svn_wc__db_with_txn(wcroot, local_relpath,
@@ -9791,6 +9870,7 @@ svn_error_t *
 svn_wc__db_scan_deletion(const char **base_del_abspath,
                          const char **moved_to_abspath,
                          const char **work_del_abspath,
+                         const char **copy_op_root_abspath,
                          svn_wc__db_t *db,
                          const char *local_abspath,
                          apr_pool_t *result_pool,
@@ -9799,6 +9879,7 @@ svn_wc__db_scan_deletion(const char **ba
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
   const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
+  const char *copy_op_root_relpath;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
@@ -9807,7 +9888,7 @@ svn_wc__db_scan_deletion(const char **ba
   VERIFY_USABLE_WCROOT(wcroot);
 
   SVN_ERR(scan_deletion(&base_del_relpath, &moved_to_relpath,
-                        &work_del_relpath, wcroot,
+                        &work_del_relpath, &copy_op_root_relpath, wcroot,
                         local_relpath, scratch_pool, scratch_pool));
 
   if (base_del_abspath)
@@ -9831,6 +9912,13 @@ svn_wc__db_scan_deletion(const char **ba
                                              work_del_relpath, result_pool)
                            : NULL);
     }
+  if (copy_op_root_abspath)
+    {
+      *copy_op_root_abspath = (copy_op_root_relpath
+                           ? svn_dirent_join(wcroot->abspath,
+                                             copy_op_root_relpath, result_pool)
+                           : NULL);
+    }
 
   return SVN_NO_ERROR;
 }

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=1152436&r1=1152435&r2=1152436&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.h Sat Jul 30 09:22:14 2011
@@ -2536,13 +2536,18 @@ svn_wc__db_scan_addition(svn_wc__db_stat
    BASE_DEL_ABSPATH will specify the nearest ancestor of the explicit or
    implicit deletion (if any) that applies to the BASE tree.
 
-   MOVED_TO_ABSPATH will specify the nearest ancestor that has moved-away,
-   if any. If no ancestors have been moved-away, then this is set to NULL.
-
    WORK_DEL_ABSPATH will specify the root of a deleted subtree within
    the WORKING tree (note there is no concept of layered delete operations
    in WORKING, so there is only one deletion root in the ancestry).
 
+   MOVED_TO_ABSPATH will specify the path where this node was moved to
+   if the node has moved-away.
+
+   If the node was moved-away, COPY_OP_ROOT_ABSPATH will specify the root
+   of the copy operation that created the copy-half of the move. 
+   If LOCAL_ABSPATH itself is the root of the copy, COPY_OP_ROOT_ABSPATH
+   equals MOVED_TO_ABSPATH.
+
    All OUT parameters may be set to NULL to indicate a lack of interest in
    that piece of information.
 
@@ -2557,6 +2562,7 @@ svn_error_t *
 svn_wc__db_scan_deletion(const char **base_del_abspath,
                          const char **moved_to_abspath,
                          const char **work_del_abspath,
+                         const char **copy_op_root_abspath,
                          svn_wc__db_t *db,
                          const char *local_abspath,
                          apr_pool_t *result_pool,

Modified: subversion/trunk/subversion/tests/libsvn_wc/db-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_wc/db-test.c?rev=1152436&r1=1152435&r2=1152436&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_wc/db-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_wc/db-test.c Sat Jul 30 09:22:14 2011
@@ -140,7 +140,7 @@ static const char * const TESTING_DATA =
   "  null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 0, 'J', 1, 'J/J-e', 1, 'normal',"
-  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, 'other/place', 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-a', 0, 'J/J-e', 1, 'J/J-e/J-e-a', 1, 'normal',"
@@ -172,7 +172,7 @@ static const char * const TESTING_DATA =
   "  15, null, null, null);"
   "insert into nodes values ("
   "  1, 'K/K-b', 0, 'K', 1, 'K/K-b', 1, 'normal',"
-  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, 'moved/away', 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "',"
   "  15, null, null, null);"
   ""
    /* Load data into NODES table;
@@ -233,7 +233,7 @@ static const char * const TESTING_DATA =
   "  10, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 1, 'J', null, null, null, 'normal',"
-  "  0, 'other/place', 'dir', '()', null, null, null, null, null, null,"
+  "  0, null, 'dir', '()', null, null, null, null, null, null,"
   "  null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-a', 1, 'J/J-e', null, null, null, 'normal',"
@@ -245,7 +245,7 @@ static const char * const TESTING_DATA =
   "  null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e', 2, 'J', null, null, null, 'base-deleted',"
-  "  0, 'other/place', 'dir', '()', null, null, null, null, null, null,"
+  "  0, null, 'dir', '()', null, null, null, null, null, null,"
   "  null, null, null, null);"
   "insert into nodes values ("
   "  1, 'J/J-e/J-e-a', 2, 'J/J-e', null, null, null, 'base-deleted',"
@@ -277,7 +277,7 @@ static const char * const TESTING_DATA =
   "  null, null, null, null);"
   "insert into nodes values ("
   "  1, 'K/K-b', 1, 'K', null, null, null, 'base-deleted',"
-  "  0, 'moved/away', 'file', '()', null, null, null, null, null, null,"
+  "  0, null, 'file', '()', null, null, null, null, null, null,"
   "  null, null, null, null);"
   "insert into nodes values ("
   "  1, 'L', 1, '', null, null, null, 'normal',"
@@ -299,6 +299,22 @@ static const char * const TESTING_DATA =
   "  1, 'L/L-a/L-a-a', 2, 'L/L-a', null, null, null, 'base-deleted',"
   "  0, null, 'dir', '()', 'immediates', null, null, null, null, null,"
   "  null, null, null, null);"
+  "insert into nodes values ("
+  "  1, 'other/place', 2, 'other', null, null, null, 'normal',"
+  "  1, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, null, null);"
+  "insert into nodes values ("
+  "  1, 'other/place/J-e-a', 2, 'other/place', null, null, null, 'normal',"
+  "  0, null, 'dir', '()', 'immediates', null, null, null, null, null,"
+  "  null, null, null, null);"
+  "insert into nodes values ("
+  "  1, 'other/place/J-e-b', 2, 'other/place', null, null, null, 'normal',"
+  "  null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  null, null, null, null);"
+  "insert into nodes values ("
+  "  1, 'other/place/J-e-b/Jeba', 0, 'other/place/J-e-b', null, null, null, 'normal',"
+  "  null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "',"
+  "  15, null, null, null);"
    "insert into actual_node values ("
    "  1, 'I', '', null, null, null, null, null, 'changelist', null, "
    "  null, null, null, null, null);"
@@ -1025,6 +1041,7 @@ test_scan_deletion(apr_pool_t *pool)
   const char *base_del_abspath;
   const char *work_del_abspath;
   const char *moved_to_abspath;
+  const char *copy_op_root_abspath;
 
   SVN_ERR(create_open(&db, &local_abspath, "test_scan_deletion", pool));
 
@@ -1033,6 +1050,7 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            &copy_op_root_abspath,
             db, svn_dirent_join(local_abspath, "J/J-e", pool),
             pool, pool));
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e",
@@ -1041,26 +1059,32 @@ test_scan_deletion(apr_pool_t *pool)
                                    moved_to_abspath, pool));
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e",
                                    work_del_abspath, pool));
+  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place",
+                                   copy_op_root_abspath, pool));
 
   /* Node was moved elsewhere (child of operation root). */
   SVN_ERR(svn_wc__db_scan_deletion(
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            &copy_op_root_abspath,
             db, svn_dirent_join(local_abspath, "J/J-e/J-e-a", pool),
             pool, pool));
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place",
+  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place/J-e-a",
                                    moved_to_abspath, pool));
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e",
                                    work_del_abspath, pool));
+  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place",
+                                   copy_op_root_abspath, pool));
 
   /* Root of delete. Parent is a WORKING node. */
   SVN_ERR(svn_wc__db_scan_deletion(
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            NULL,
             db, svn_dirent_join(local_abspath, "J/J-c", pool),
             pool, pool));
   /* Implicit delete of "J" (via replacement).  */
@@ -1075,6 +1099,7 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            NULL,
             db, svn_dirent_join(local_abspath, "J/J-c/J-c-a", pool),
             pool, pool));
   /* Implicit delete of "J" (via replacement).  */
@@ -1089,6 +1114,7 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            NULL,
             db, svn_dirent_join(local_abspath, "J/J-e/J-e-b/Jeba", pool),
             pool, pool));
   /* ### I don't understand this.  "J/J-e/J-e-b/Jeba" is a deleted
@@ -1096,7 +1122,7 @@ test_scan_deletion(apr_pool_t *pool)
      Why does base_del_abspath refer to "J-e"?  */
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e",
                                    base_del_abspath, pool));
-  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place",
+  SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place/J-e-b/Jeba",
                                    moved_to_abspath, pool));
   SVN_TEST_ASSERT(work_del_abspath == NULL);
 
@@ -1105,6 +1131,7 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            NULL,
             db, svn_dirent_join(local_abspath, "J/J-f/J-f-a", pool),
             pool, pool));
   /* Implicit delete of "J" (via replacement).  */
@@ -1118,6 +1145,7 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            NULL,
             db, svn_dirent_join(local_abspath, "K", pool),
             pool, pool));
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "K",
@@ -1130,6 +1158,7 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            NULL,
             db, svn_dirent_join(local_abspath, "K/K-a", pool),
             pool, pool));
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "K",
@@ -1142,12 +1171,15 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            &copy_op_root_abspath,
             db, svn_dirent_join(local_abspath, "K/K-b", pool),
             pool, pool));
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "K/K-b",
                                    base_del_abspath, pool));
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/away",
                                    moved_to_abspath, pool));
+  SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/away",
+                                   copy_op_root_abspath, pool));
   SVN_TEST_ASSERT(work_del_abspath == NULL);
 
   /* Subtree deletion of added tree. Start at child.  */
@@ -1155,6 +1187,7 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            NULL,
             db, svn_dirent_join(local_abspath, "L/L-a/L-a-a", pool),
             pool, pool));
   SVN_TEST_ASSERT(base_del_abspath == NULL);
@@ -1167,6 +1200,7 @@ test_scan_deletion(apr_pool_t *pool)
             &base_del_abspath,
             &moved_to_abspath,
             &work_del_abspath,
+            NULL,
             db, svn_dirent_join(local_abspath, "L/L-a", pool),
             pool, pool));
   SVN_TEST_ASSERT(base_del_abspath == NULL);