You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/02/09 18:27:57 UTC

svn commit: r1658479 - 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: rhuijben
Date: Mon Feb  9 17:27:56 2015
New Revision: 1658479

URL: http://svn.apache.org/r1658479
Log:
Fix yet another case where we corrupted the move tracking information during
conflict resolving. When breaking a move it was possible that moved_here
was reset, but moved_to kept.

As part of this fix I made two database statements more similar.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_SELECT_OP_DEPTH_MOVED_PAIR): Rename to...
  (STMT_SELECT_MOVED_DESCENDANTS_SHD): and rename some aliases.

  (STMT_SELECT_MOVED_DESCENDANTS): Rename to...
  (STMT_SELECT_MOVED_DESCENDANTS_SRC): and update query layout and result
    columns.

* subversion/libsvn_wc/wc_db.c
  (op_revert_txn): Update caller to avoid db query that might fail to
    obtain information we already have.
  (moved_descendant_commit): Update statement usage.

* subversion/libsvn_wc/wc_db_private.h
  (svn_wc__db_resolve_break_moved_away_internal): Remove function.
  (svn_wc__db_op_break_move_internal): New function.

* subversion/libsvn_wc/wc_db_update_move.c
  (resolve_delete_raise_moved_away): Update query usage.

  (break_move): Rename to...
  (svn_wc__db_op_break_move_internal): ... this and verify if the move
    source actually exists (which in some cases it didn't)

  (svn_wc__db_resolve_break_moved_away_internal): Rename to...
  (break_moved_away): ... this. Update caller.

  (break_moved_away_children_internal): Rename to...
  (break_moved_away_children): ... this. Update statement usage.
    The original code read a non existing op-depth column and as
    such always got op-depth 0.
  (svn_wc__db_resolve_break_moved_away,
   svn_wc__db_resolve_break_moved_away_children): Update caller.

* subversion/tests/libsvn_wc/op-depth-test.c
  (del4_update_edit_AAA): Extend test. The original version had an
    assumption on the bug, but masked the real problem because it
    only tested the move source.

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=1658479&r1=1658478&r2=1658479&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Mon Feb  9 17:27:56 2015
@@ -1651,33 +1651,38 @@ WHERE wc_id = ?1
   AND moved_to IS NOT NULL
   AND NOT IS_STRICT_DESCENDANT_OF(moved_to, ?2)
 
--- STMT_SELECT_OP_DEPTH_MOVED_PAIR
-SELECT n.local_relpath, p.kind, n.moved_to, p.repos_path
-FROM nodes n
-JOIN nodes p ON p.wc_id = ?1 AND p.local_relpath = ?2
- AND p.op_depth=(SELECT MAX(d.op_depth)
+/* This statement is very similar to STMT_SELECT_MOVED_DESCENDANTS_SRC
+   but the passed op-depth is the depth of the shadowing node.
+
+   This version is slightly more efficient as the most inner query is
+   only executed once, but the Sqlite page cache makes the difference
+   not really measurable */
+-- STMT_SELECT_MOVED_DESCENDANTS_SHD
+SELECT n.op_depth, n.local_relpath, n.kind, n.repos_path, s.moved_to
+FROM nodes s
+JOIN nodes n ON n.wc_id = ?1 AND n.local_relpath = s.local_relpath
+ AND n.op_depth=(SELECT MAX(d.op_depth)
                  FROM nodes d
                  WHERE d.wc_id = ?1 AND d.local_relpath = ?2
                    AND d.op_depth < ?3)
-WHERE n.wc_id = ?1
-  AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
-  AND n.op_depth = ?3
-  AND n.moved_to IS NOT NULL
+WHERE s.wc_id = ?1 AND s.op_depth = ?3
+  AND IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2)
+  AND s.moved_to IS NOT NULL
 
--- STMT_SELECT_MOVED_DESCENDANTS
-SELECT n.local_relpath, h.moved_to
-FROM nodes n, nodes h
-WHERE n.wc_id = ?1
-  AND h.wc_id = ?1
+/* This statement is very similar to STMT_SELECT_MOVED_DESCENDANTS_SHD,
+   but the passed op-depth is the depth of the node as it originally existed */
+-- STMT_SELECT_MOVED_DESCENDANTS_SRC
+SELECT s.op_depth, n.local_relpath, n.kind, n.repos_path, s.moved_to
+FROM nodes n
+JOIN nodes s ON s.wc_id = n.wc_id AND s.local_relpath = n.local_relpath
+ AND s.op_depth = (SELECT MIN(d.op_depth)
+                    FROM nodes d
+                    WHERE d.wc_id = ?1
+                      AND d.local_relpath = s.local_relpath
+                      AND d.op_depth > ?3)
+WHERE n.wc_id = ?1 AND n.op_depth = ?3
   AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
-  AND h.local_relpath = n.local_relpath
-  AND n.op_depth = ?3
-  AND h.op_depth = (SELECT MIN(o.op_depth)
-                    FROM nodes o
-                    WHERE o.wc_id = ?1
-                      AND o.local_relpath = n.local_relpath
-                      AND o.op_depth > ?3)
-  AND h.moved_to IS NOT NULL
+  AND s.moved_to IS NOT NULL
 
 -- STMT_COMMIT_UPDATE_ORIGIN
 UPDATE nodes SET repos_id = ?4,

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1658479&r1=1658478&r2=1658479&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Mon Feb  9 17:27:56 2015
@@ -6794,10 +6794,9 @@ op_revert_txn(void *baton,
 
   if (moved_to)
     {
-      SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
-                                                           local_relpath,
-                                                           op_depth,
-                                                           scratch_pool));
+      SVN_ERR(svn_wc__db_op_break_move_internal(wcroot,
+                                                local_relpath, op_depth,
+                                                moved_to, scratch_pool));
     }
   else
     {
@@ -6973,14 +6972,14 @@ op_revert_recursive_txn(void *baton,
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   while (have_row)
     {
-      const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL);
       int move_op_depth = svn_sqlite__column_int(stmt, 2);
       svn_error_t *err;
 
-      err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
-                                                         move_src_relpath,
-                                                         move_op_depth,
-                                                         scratch_pool);
+      err = svn_wc__db_op_break_move_internal(wcroot,
+                                              src_relpath, move_op_depth,
+                                              dst_relpath, scratch_pool);
       if (err)
         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
 
@@ -11319,7 +11318,7 @@ moved_descendant_commit(svn_wc__db_wcroo
                  && *repos_relpath != '\0');
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_MOVED_DESCENDANTS));
+                                    STMT_SELECT_MOVED_DESCENDANTS_SRC));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
                                          local_relpath,
                                          op_depth));
@@ -11330,12 +11329,12 @@ moved_descendant_commit(svn_wc__db_wcroo
 
   children = apr_hash_make(scratch_pool);
 
-  /* First, obtain all moved children */
+  /* First, obtain all moved descendants */
   /* To keep error handling simple, first cache them in a hashtable */
   while (have_row)
     {
-      const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
-      const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
+      const char *src_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
+      const char *to_relpath = svn_sqlite__column_text(stmt, 4, scratch_pool);
 
       svn_hash_sets(children, src_relpath, to_relpath);
 

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=1658479&r1=1658478&r2=1658479&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_private.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_private.h Mon Feb  9 17:27:56 2015
@@ -496,12 +496,14 @@ svn_wc__db_bump_moved_away(svn_wc__db_wc
                            apr_pool_t *scratch_pool);
 
 /* Unbreak the move from LOCAL_RELPATH on op-depth in WCROOT, by making
-   the destination a normal copy */
+   the destination DST_RELPATH a normal copy. SRC_OP_DEPTH is the op-depth
+   where the move_to information is stored */
 svn_error_t *
-svn_wc__db_resolve_break_moved_away_internal(svn_wc__db_wcroot_t *wcroot,
-                                             const char *local_relpath,
-                                             int op_depth,
-                                             apr_pool_t *scratch_pool);
+svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
+                                  const char *src_relpath,
+                                  int src_op_depth,
+                                  const char *dst_relpath,
+                                  apr_pool_t *scratch_pool);
 
 svn_error_t *
 svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot,

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=1658479&r1=1658478&r2=1658479&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db_update_move.c Mon Feb  9 17:27:56 2015
@@ -2341,17 +2341,17 @@ resolve_delete_raise_moved_away(svn_wc__
                                       STMT_CREATE_UPDATE_MOVE_LIST));
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_OP_DEPTH_MOVED_PAIR));
+                                    STMT_SELECT_MOVED_DESCENDANTS_SHD));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
                             relpath_depth(local_relpath)));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   while(have_row)
     {
       svn_error_t *err;
-      const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
-      svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 1, kind_map);
-      const char *dst_relpath = svn_sqlite__column_text(stmt, 2, NULL);
+      const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL);
+      svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map);
       const char *src_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
+      const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL);
       svn_skel_t *conflict;
       svn_pool_clear(iterpool);
 
@@ -2429,20 +2429,27 @@ svn_wc__db_resolve_delete_raise_moved_aw
   return SVN_NO_ERROR;
 }
 
-static svn_error_t *
-break_move(svn_wc__db_wcroot_t *wcroot,
-           const char *src_relpath,
-           int src_op_depth,
-           const char *dst_relpath,
-           apr_pool_t *scratch_pool)
+svn_error_t *
+svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
+                                  const char *src_relpath,
+                                  int src_op_depth,
+                                  const char *dst_relpath,
+                                  apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
+  int affected;
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_CLEAR_MOVED_TO_RELPATH));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
                             src_op_depth));
-  SVN_ERR(svn_sqlite__step_done(stmt));
+  SVN_ERR(svn_sqlite__update(&affected, stmt));
+
+  if (affected != 1)
+    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+                             _("Path '%s' is not moved"),
+                             path_for_error_message(wcroot, src_relpath,
+                                                    scratch_pool));
 
   /* The destination is always an op-root, so we can calculate the depth
      from there. */
@@ -2455,11 +2462,11 @@ break_move(svn_wc__db_wcroot_t *wcroot,
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_wc__db_resolve_break_moved_away_internal(svn_wc__db_wcroot_t *wcroot,
-                                             const char *local_relpath,
-                                             int op_depth,
-                                             apr_pool_t *scratch_pool)
+static svn_error_t *
+break_moved_away(svn_wc__db_wcroot_t *wcroot,
+                 const char *local_relpath,
+                 int op_depth,
+                 apr_pool_t *scratch_pool)
 {
   const char *dummy1, *move_dst_op_root_relpath;
   const char *dummy2, *move_src_op_root_relpath;
@@ -2475,18 +2482,18 @@ svn_wc__db_resolve_break_moved_away_inte
   SVN_ERR_ASSERT(move_src_op_root_relpath != NULL
                  && move_dst_op_root_relpath != NULL);
 
-  SVN_ERR(break_move(wcroot, local_relpath,
-                     relpath_depth(move_src_op_root_relpath),
-                     move_dst_op_root_relpath,
-                     scratch_pool));
+  SVN_ERR(svn_wc__db_op_break_move_internal(wcroot, local_relpath,
+                                            relpath_depth(move_src_op_root_relpath),
+                                            move_dst_op_root_relpath,
+                                            scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
-break_moved_away_children_internal(svn_wc__db_wcroot_t *wcroot,
-                                   const char *local_relpath,
-                                   apr_pool_t *scratch_pool)
+break_moved_away_children(svn_wc__db_wcroot_t *wcroot,
+                          const char *local_relpath,
+                          apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
@@ -2496,7 +2503,7 @@ break_moved_away_children_internal(svn_w
                                       STMT_CREATE_UPDATE_MOVE_LIST));
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_MOVED_DESCENDANTS));
+                                    STMT_SELECT_MOVED_DESCENDANTS_SRC));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
                             relpath_depth(local_relpath)));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
@@ -2504,15 +2511,16 @@ break_moved_away_children_internal(svn_w
   iterpool = svn_pool_create(scratch_pool);
   while (have_row)
     {
-      const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
-      const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL);
-      int src_op_depth = svn_sqlite__column_int(stmt, 2);
+      int src_op_depth = svn_sqlite__column_int(stmt, 0) ;
+      const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL);
+      const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL);
       svn_error_t *err;
 
       svn_pool_clear(iterpool);
 
-      err = break_move(wcroot, src_relpath, src_op_depth, dst_relpath,
-                       iterpool);
+      err = svn_wc__db_op_break_move_internal(wcroot,
+                                              src_relpath, src_op_depth,
+                                              dst_relpath, iterpool);
 
       if (! err)
         {
@@ -2560,11 +2568,10 @@ svn_wc__db_resolve_break_moved_away(svn_
   src_relpath = svn_dirent_skip_ancestor(wcroot->abspath, src_op_root_abspath);
   SVN_ERR_ASSERT(src_relpath != NULL);
 
-  SVN_WC__DB_WITH_TXN(
-    svn_wc__db_resolve_break_moved_away_internal(wcroot, local_relpath,
-                                                 relpath_depth(src_relpath),
-                                                 scratch_pool),
-    wcroot);
+  SVN_WC__DB_WITH_TXN(break_moved_away(wcroot, local_relpath,
+                                       relpath_depth(src_relpath),
+                                       scratch_pool),
+                      wcroot);
 
   if (notify_func)
     {
@@ -2602,7 +2609,7 @@ svn_wc__db_resolve_break_moved_away_chil
   VERIFY_USABLE_WCROOT(wcroot);
 
   SVN_WC__DB_WITH_TXN(
-    break_moved_away_children_internal(wcroot, local_relpath, scratch_pool),
+    break_moved_away_children(wcroot, local_relpath, scratch_pool),
     wcroot);
 
   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,

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=1658479&r1=1658478&r2=1658479&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c Mon Feb  9 17:27:56 2015
@@ -9266,46 +9266,181 @@ del4_update_edit_AAA(const svn_test_opts
 
   {
     nodes_row_t nodes[] = {
-
-      { 0, "A/A/A",     "normal",    2, "A/A/A", NOT_MOVED,            "key" },
-      { 1, "A/A/A",     "normal",    2, "B/A/A", FALSE, "AAA_1", TRUE, "key" },
-      { 2, "A/A/A",     "normal",    2, "C/A/A", FALSE, "AAA_2", TRUE, "key" },
-      { 3, "A/A/A",     "normal",    2, "D/A/A", FALSE, "AAA_3", TRUE, "key" },
-
-      { 0, "A/A/A/A",   "normal",    2, "A/A/A/A", NOT_MOVED,          "key" },
-      { 1, "A/A/A/A",   "normal",    2, "B/A/A/A", FALSE, NULL, TRUE,  "key" },
-      { 2, "A/A/A/A",   "normal",    2, "C/A/A/A", FALSE, NULL, TRUE,  "key" },
-      { 3, "A/A/A/A",   "normal",    2, "D/A/A/A", FALSE, NULL, TRUE,  "key" },
+      {0, "",                 "normal",       2, ""},
+      {0, "A",                "normal",       2, "A"},
+      {0, "A/A",              "normal",       2, "A/A"},
+      {0, "A/A/A",            "normal",       2, "A/A/A", NOT_MOVED, "key"},
+      {0, "A/A/A/A",          "normal",       2, "A/A/A/A", NOT_MOVED, "key"},
+      {0, "B",                "normal",       2, "B"},
+      {0, "B/A",              "normal",       2, "B/A"},
+      {0, "B/A/A",            "normal",       2, "B/A/A", NOT_MOVED, "key"},
+      {0, "B/A/A/A",          "normal",       2, "B/A/A/A", NOT_MOVED, "key"},
+      {0, "C",                "normal",       2, "C"},
+      {0, "C/A",              "normal",       2, "C/A"},
+      {0, "C/A/A",            "normal",       2, "C/A/A", NOT_MOVED, "key"},
+      {0, "C/A/A/A",          "normal",       2, "C/A/A/A", NOT_MOVED, "key"},
+      {0, "D",                "normal",       2, "D"},
+      {0, "D/A",              "normal",       2, "D/A"},
+      {0, "D/A/A",            "normal",       2, "D/A/A", NOT_MOVED, "key"},
+      {0, "D/A/A/A",          "normal",       2, "D/A/A/A", NOT_MOVED, "key"},
+
+      {1, "A",                "normal",       2, "B", MOVED_HERE},
+      {1, "A/A",              "normal",       2, "B/A", MOVED_HERE},
+      {1, "A/A/A",            "normal",       2, "B/A/A", FALSE, "AAA_1", TRUE, "key"},
+      {1, "A/A/A/A",          "normal",       2, "B/A/A/A", FALSE, NULL, TRUE, "key"},
+      {1, "AAA_1",            "normal",       2, "A/A/A", MOVED_HERE, "key"},
+      {1, "AAA_1/A",          "normal",       2, "A/A/A/A", MOVED_HERE, "key"},
+      {1, "AAA_2",            "normal",       2, "B/A/A", MOVED_HERE, "key"},
+      {1, "AAA_2/A",          "normal",       2, "B/A/A/A", MOVED_HERE, "key"},
+      {1, "AAA_3",            "normal",       2, "C/A/A", MOVED_HERE, "key"},
+      {1, "AAA_3/A",          "normal",       2, "C/A/A/A", MOVED_HERE, "key"},
+      {1, "B",                "base-deleted", NO_COPY_FROM, "A"},
+      {1, "B/A",              "base-deleted", NO_COPY_FROM},
+      {1, "B/A/A",            "base-deleted", NO_COPY_FROM},
+      {1, "B/A/A/A",          "base-deleted", NO_COPY_FROM},
+
+      {2, "A/A",              "normal",       2, "C/A", MOVED_HERE},
+      {2, "A/A/A",            "normal",       2, "C/A/A", FALSE, "AAA_2", TRUE, "key"},
+      {2, "A/A/A/A",          "normal",       2, "C/A/A/A", FALSE, NULL, TRUE,  "key"},
+      {2, "C/A",              "base-deleted", NO_COPY_FROM, "A/A"},
+      {2, "C/A/A",            "base-deleted", NO_COPY_FROM},
+      {2, "C/A/A/A",          "base-deleted", NO_COPY_FROM},
+
+      {3, "A/A/A",            "normal",       2, "D/A/A", FALSE, "AAA_3", TRUE, "key"},
+      {3, "A/A/A/A",          "normal",       2, "D/A/A/A", FALSE, NULL, TRUE,  "key"},
+      {3, "D/A/A",            "base-deleted", NO_COPY_FROM, "A/A/A"},
+      {3, "D/A/A/A",          "base-deleted", NO_COPY_FROM},
 
       { 0 },
     };
 
-    SVN_ERR(check_db_rows(&b, "A/A/A", nodes));
+    SVN_ERR(check_db_rows(&b, "", nodes));
+    SVN_ERR(check_db_conflicts(&b, "", NULL));
   }
 
   /* Go back to start position */
   SVN_ERR(sbox_wc_update(&b, "", 1));
   SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_mine_conflict));
+
+  {
+    nodes_row_t nodes[] = {
+      {0, "",                 "normal",       1, ""},
+      {0, "A",                "normal",       1, "A"},
+      {0, "A/A",              "normal",       1, "A/A"},
+      {0, "A/A/A",            "normal",       1, "A/A/A", NOT_MOVED},
+      {0, "A/A/A/A",          "normal",       1, "A/A/A/A", NOT_MOVED},
+      {0, "B",                "normal",       1, "B"},
+      {0, "B/A",              "normal",       1, "B/A"},
+      {0, "B/A/A",            "normal",       1, "B/A/A", NOT_MOVED},
+      {0, "B/A/A/A",          "normal",       1, "B/A/A/A", NOT_MOVED},
+      {0, "C",                "normal",       1, "C"},
+      {0, "C/A",              "normal",       1, "C/A"},
+      {0, "C/A/A",            "normal",       1, "C/A/A", NOT_MOVED},
+      {0, "C/A/A/A",          "normal",       1, "C/A/A/A", NOT_MOVED},
+      {0, "D",                "normal",       1, "D"},
+      {0, "D/A",              "normal",       1, "D/A"},
+      {0, "D/A/A",            "normal",       1, "D/A/A", NOT_MOVED},
+      {0, "D/A/A/A",          "normal",       1, "D/A/A/A", NOT_MOVED},
+
+      {1, "A",                "normal",       1, "B", MOVED_HERE},
+      {1, "A/A",              "normal",       1, "B/A", MOVED_HERE},
+      {1, "A/A/A",            "normal",       1, "B/A/A", FALSE, "AAA_1", TRUE},
+      {1, "A/A/A/A",          "normal",       1, "B/A/A/A", FALSE, NULL, TRUE},
+      {1, "AAA_1",            "normal",       1, "A/A/A", MOVED_HERE},
+      {1, "AAA_1/A",          "normal",       1, "A/A/A/A", MOVED_HERE},
+      {1, "AAA_2",            "normal",       1, "B/A/A", MOVED_HERE},
+      {1, "AAA_2/A",          "normal",       1, "B/A/A/A", MOVED_HERE},
+      {1, "AAA_3",            "normal",       1, "C/A/A", MOVED_HERE},
+      {1, "AAA_3/A",          "normal",       1, "C/A/A/A", MOVED_HERE},
+      {1, "B",                "base-deleted", NO_COPY_FROM, "A"},
+      {1, "B/A",              "base-deleted", NO_COPY_FROM},
+      {1, "B/A/A",            "base-deleted", NO_COPY_FROM},
+      {1, "B/A/A/A",          "base-deleted", NO_COPY_FROM},
+
+      {2, "A/A",              "normal",       1, "C/A", MOVED_HERE},
+      {2, "A/A/A",            "normal",       1, "C/A/A", FALSE, "AAA_2", TRUE},
+      {2, "A/A/A/A",          "normal",       1, "C/A/A/A", FALSE, NULL, TRUE},
+      {2, "C/A",              "base-deleted", NO_COPY_FROM, "A/A"},
+      {2, "C/A/A",            "base-deleted", NO_COPY_FROM},
+      {2, "C/A/A/A",          "base-deleted", NO_COPY_FROM},
+
+      {3, "A/A/A",            "normal",       1, "D/A/A", FALSE, "AAA_3", TRUE},
+      {3, "A/A/A/A",          "normal",       1, "D/A/A/A", FALSE, NULL, TRUE},
+      {3, "D/A/A",            "base-deleted", NO_COPY_FROM, "A/A/A"},
+      {3, "D/A/A/A",          "base-deleted", NO_COPY_FROM},
+
+      { 0 },
+    };
+
+    SVN_ERR(check_db_rows(&b, "", nodes));
+    SVN_ERR(check_db_conflicts(&b, "", NULL));
+  }
+
   /* Update and resolve via their strategy */
   SVN_ERR(sbox_wc_update(&b, "", 2));
+  {
+    conflict_info_t conflicts[] = {
+      {"A", FALSE, FALSE, TRUE},
+      {"B", FALSE, FALSE, TRUE},
+      {"C/A", FALSE, FALSE, TRUE},
+      {"D/A/A", FALSE, FALSE, TRUE},
+
+      {0},
+    };
+
+    SVN_ERR(check_db_conflicts(&b, "", conflicts));
+  }
+  /* This breaks the move from D/A/A -> A/A/A */
   SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, svn_wc_conflict_choose_merged));
 
   {
     nodes_row_t nodes[] = {
-
-      { 0, "A/A/A",     "normal",    2, "A/A/A", NOT_MOVED, "key" },
-      { 1, "A/A/A",     "normal",    1, "B/A/A", FALSE, "AAA_1" },
-      { 2, "A/A/A",     "normal",    1, "C/A/A", FALSE, "AAA_2" },
-      { 3, "A/A/A",     "normal",    1, "D/A/A", FALSE, "AAA_3" },
-
-      { 0, "A/A/A/A",   "normal",    2, "A/A/A/A", NOT_MOVED, "key" },
-      { 1, "A/A/A/A",   "normal",    1, "B/A/A/A" },
-      { 2, "A/A/A/A",   "normal",    1, "C/A/A/A" },
-      { 3, "A/A/A/A",   "normal",    1, "D/A/A/A" },
+      {0, "",          "normal",       2, ""},
+      {0, "A",         "normal",       2, "A"},
+      {0, "A/A",       "normal",       2, "A/A"},
+      {0, "A/A/A",     "normal",       2, "A/A/A", NOT_MOVED, "key"},
+      {0, "A/A/A/A",   "normal",       2, "A/A/A/A", NOT_MOVED, "key"},
+      {0, "B",         "normal",       2, "B"},
+      {0, "B/A",       "normal",       2, "B/A"},
+      {0, "B/A/A",     "normal",       2, "B/A/A", NOT_MOVED, "key"},
+      {0, "B/A/A/A",   "normal",       2, "B/A/A/A", NOT_MOVED, "key"},
+      {0, "C",         "normal",       2, "C"},
+      {0, "C/A",       "normal",       2, "C/A"},
+      {0, "C/A/A",     "normal",       2, "C/A/A", NOT_MOVED, "key"},
+      {0, "C/A/A/A",   "normal",       2, "C/A/A/A", NOT_MOVED, "key"},
+      {0, "D",         "normal",       2, "D"},
+      {0, "D/A",       "normal",       2, "D/A"},
+      {0, "D/A/A",     "normal",       2, "D/A/A", NOT_MOVED, "key"},
+      {0, "D/A/A/A",   "normal",       2, "D/A/A/A", NOT_MOVED, "key"},
+      {1, "A",         "normal",       1, "B"},
+      {1, "A/A",       "normal",       1, "B/A"},
+      {1, "A/A/A",     "normal",       1, "B/A/A", FALSE, "AAA_1"},
+      {1, "A/A/A/A",   "normal",       1, "B/A/A/A"},
+      {1, "AAA_1",     "normal",       1, "A/A/A", MOVED_HERE},
+      {1, "AAA_1/A",   "normal",       1, "A/A/A/A", MOVED_HERE},
+      {1, "AAA_2",     "normal",       1, "B/A/A"},
+      {1, "AAA_2/A",   "normal",       1, "B/A/A/A"},
+      {1, "AAA_3",     "normal",       1, "C/A/A", MOVED_HERE},
+      {1, "AAA_3/A",   "normal",       1, "C/A/A/A", MOVED_HERE},
+      {1, "B",         "base-deleted", NO_COPY_FROM},
+      {1, "B/A",       "base-deleted", NO_COPY_FROM},
+      {1, "B/A/A",     "base-deleted", NO_COPY_FROM},
+      {1, "B/A/A/A",   "base-deleted", NO_COPY_FROM},
+      {2, "A/A",       "normal",       1, "C/A"},
+      {2, "A/A/A",     "normal",       1, "C/A/A"},
+      {2, "A/A/A/A",   "normal",       1, "C/A/A/A"},
+      {2, "C/A",       "base-deleted", NO_COPY_FROM},
+      {2, "C/A/A",     "base-deleted", NO_COPY_FROM},
+      {2, "C/A/A/A",   "base-deleted", NO_COPY_FROM},
+      {3, "A/A/A",     "normal",       1, "D/A/A", FALSE, "AAA_3"},
+      {3, "A/A/A/A",   "normal",       1, "D/A/A/A"},
+      {3, "D/A/A",     "base-deleted", NO_COPY_FROM},
+      {3, "D/A/A/A",   "base-deleted", NO_COPY_FROM},
 
       { 0 },
     };
-      SVN_ERR(check_db_rows(&b, "A/A/A", nodes));
+
+    SVN_ERR(check_db_rows(&b, "", nodes));
   }