You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2013/10/22 19:01:43 UTC

svn commit: r1534703 - in /subversion/branches/1.8.x: ./ subversion/libsvn_wc/ subversion/tests/cmdline/ subversion/tests/libsvn_wc/

Author: breser
Date: Tue Oct 22 17:01:42 2013
New Revision: 1534703

URL: http://svn.apache.org/r1534703
Log:
Merge the r1530763 group from trunk:

 * r1530763, r1530768, r1530922, r1530872, r1531002, r1531004, r1531938, r1532098, r1530967
   Resolve issue #4436, reverting a move leaves some move data.
   Justification:
      Reverting a move shouldn't break the wc.db state.
   Notes:
      breser: Added r1530967 because the comment in wc_db.h mentions the test
      added by that revision.
   Votes:
     +1: breser, rhuijben, philip


Modified:
    subversion/branches/1.8.x/   (props changed)
    subversion/branches/1.8.x/STATUS
    subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql
    subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c
    subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h
    subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_private.h
    subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_update_move.c
    subversion/branches/1.8.x/subversion/tests/cmdline/move_tests.py
    subversion/branches/1.8.x/subversion/tests/cmdline/revert_tests.py
    subversion/branches/1.8.x/subversion/tests/libsvn_wc/op-depth-test.c

Propchange: subversion/branches/1.8.x/
------------------------------------------------------------------------------
  Merged /subversion/trunk:r1530763,1530768,1530872,1530922,1530967,1531002,1531004,1531938,1532098

Modified: subversion/branches/1.8.x/STATUS
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/STATUS?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/STATUS (original)
+++ subversion/branches/1.8.x/STATUS Tue Oct 22 17:01:42 2013
@@ -107,16 +107,6 @@ Veto-blocked changes:
 Approved changes:
 =================
 
- * r1530763, r1530768, r1530922, r1530872, r1531002, r1531004, r1531938, r1532098, r1530967
-   Resolve issue #4436, reverting a move leaves some move data.
-   Justification:
-      Reverting a move shouldn't break the wc.db state.
-   Notes:
-      breser: Added r1530967 because the comment in wc_db.h mentions the test
-      added by that revision.
-   Votes:
-     +1: breser, rhuijben, philip
-
  * r1520065, r1520745
    Resolve a user triggerable assertion when during an update a file external
    is being replaced by a server excluded (aka absent) node.  Issue 4422.

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/wc-queries.sql Tue Oct 22 17:01:42 2013
@@ -1571,7 +1571,7 @@ WHERE wc_id = ?1
   AND moved_to IS NOT NULL
 
 -- STMT_SELECT_MOVED_OUTSIDE
-SELECT local_relpath, moved_to FROM nodes
+SELECT local_relpath, moved_to, op_depth FROM nodes
 WHERE wc_id = ?1
   AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth >= ?3

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.c Tue Oct 22 17:01:42 2013
@@ -2248,6 +2248,12 @@ db_base_remove(svn_wc__db_wcroot_t *wcro
        * might introduce actual-only nodes without direct parents,
        * and we're not yet sure if other existing code is prepared
        * to handle such nodes. To be revisited post-1.8.
+       *
+       * ### In case of a conflict we are most likely creating WORKING nodes
+       *     describing a copy of what was in BASE. The move information
+       *     should be updated to describe a move from the WORKING layer.
+       *     When stored that way the resolver of the tree conflict still has
+       *     the knowledge of what was moved.
        */
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                         STMT_SELECT_MOVED_OUTSIDE));
@@ -6388,6 +6394,7 @@ op_revert_txn(void *baton,
     {
       SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
                                                            local_relpath,
+                                                           op_depth,
                                                            scratch_pool));
     }
   else
@@ -6554,10 +6561,12 @@ op_revert_recursive_txn(void *baton,
   while (have_row)
     {
       const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, 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);
       if (err)
         return svn_error_compose_create(err, svn_sqlite__reset(stmt));

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/wc_db.h Tue Oct 22 17:01:42 2013
@@ -3382,7 +3382,14 @@ svn_wc__db_resolve_delete_raise_moved_aw
                                            apr_pool_t *scratch_pool);
 
 /* Like svn_wc__db_resolve_delete_raise_moved_away this should be
-   combined. */
+   combined.
+   
+   ### LOCAL_ABSPATH specifies the move origin, but the move origin
+   ### is not necessary unique enough. This function needs an op_root_abspath
+   ### argument to differentiate between different origins.
+
+   ### See move_tests.py: move_many_update_delete for an example case.
+   */
 svn_error_t *
 svn_wc__db_resolve_break_moved_away(svn_wc__db_t *db,
                                     const char *local_abspath,

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_private.h?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_private.h (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_private.h Tue Oct 22 17:01:42 2013
@@ -442,9 +442,12 @@ svn_wc__db_bump_moved_away(svn_wc__db_wc
                            svn_wc__db_t *db,
                            apr_pool_t *scratch_pool);
 
+/* Unbreak the move from LOCAL_RELPATH on op-depth in WCROOT, by making
+   the destination a normal copy */
 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_error_t *

Modified: subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_update_move.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_update_move.c?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_update_move.c (original)
+++ subversion/branches/1.8.x/subversion/libsvn_wc/wc_db_update_move.c Tue Oct 22 17:01:42 2013
@@ -2284,30 +2284,34 @@ svn_wc__db_bump_moved_away(svn_wc__db_wc
                            svn_wc__db_t *db,
                            apr_pool_t *scratch_pool)
 {
-  const char *dummy1, *move_dst_op_root_relpath;
-  const char *move_src_root_relpath, *move_src_op_root_relpath;
   apr_hash_t *src_done;
 
   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
                                       STMT_CREATE_UPDATE_MOVE_LIST));
 
-  SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath,
-                                       &move_src_root_relpath,
-                                       &move_src_op_root_relpath, 0,
-                                       wcroot, local_relpath,
-                                       scratch_pool, scratch_pool));
-
-  if (move_src_root_relpath)
+  if (local_relpath[0] != '\0')
     {
-      if (strcmp(move_src_root_relpath, local_relpath))
+      const char *dummy1, *move_dst_op_root_relpath;
+      const char *move_src_root_relpath, *move_src_op_root_relpath;
+
+      /* Is the root of the update moved away? (Impossible for the wcroot) */
+      SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath,
+                                           &move_src_root_relpath,
+                                           &move_src_op_root_relpath, 0,
+                                           wcroot, local_relpath,
+                                           scratch_pool, scratch_pool));
+
+      if (move_src_root_relpath)
         {
-          SVN_ERR(bump_mark_tree_conflict(wcroot, move_src_root_relpath,
-                                          move_src_op_root_relpath,
-                                          move_dst_op_root_relpath,
-                                          db, scratch_pool));
-          return SVN_NO_ERROR;
+          if (strcmp(move_src_root_relpath, local_relpath))
+            {
+              SVN_ERR(bump_mark_tree_conflict(wcroot, move_src_root_relpath,
+                                              move_src_op_root_relpath,
+                                              move_dst_op_root_relpath,
+                                              db, scratch_pool));
+              return SVN_NO_ERROR;
+            }
         }
-
     }
 
   src_done = apr_hash_make(scratch_pool);
@@ -2440,17 +2444,23 @@ break_move(svn_wc__db_wcroot_t *wcroot,
 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)
 {
   const char *dummy1, *move_dst_op_root_relpath;
   const char *dummy2, *move_src_op_root_relpath;
 
+  /* We want to include the passed op-depth, but the function does a > check */
   SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath,
                                        &dummy2,
                                        &move_src_op_root_relpath,
-                                       relpath_depth(local_relpath) - 1,
+                                       op_depth - 1,
                                        wcroot, local_relpath,
                                        scratch_pool, scratch_pool));
+
+  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,
@@ -2519,6 +2529,7 @@ svn_wc__db_resolve_break_moved_away(svn_
 
   SVN_WC__DB_WITH_TXN(
     svn_wc__db_resolve_break_moved_away_internal(wcroot, local_relpath,
+                                                 relpath_depth(local_relpath),
                                                  scratch_pool),
     wcroot);
 

Modified: subversion/branches/1.8.x/subversion/tests/cmdline/move_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/tests/cmdline/move_tests.py?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/tests/cmdline/move_tests.py (original)
+++ subversion/branches/1.8.x/subversion/tests/cmdline/move_tests.py Tue Oct 22 17:01:42 2013
@@ -1234,6 +1234,169 @@ def move_missing(sbox):
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
 
+def setup_move_many(sbox):
+  "helper function which creates a wc with node A/A/A which is moved 3 times"
+
+  sbox.simple_rm('A', 'iota')
+  sbox.simple_mkdir('A',
+                    'A/A',
+                    'A/A/A',
+                    'A/A/A/A',
+                    'B',
+                    'B/A',
+                    'B/A/A',
+                    'B/A/A/A',
+                    'C',
+                    'C/A',
+                    'C/A/A',
+                    'C/A/A/A')
+  sbox.simple_commit()
+  sbox.simple_update()
+
+  sbox.simple_move('A/A/A', 'AAA_1')
+
+  sbox.simple_rm('A')
+  sbox.simple_move('B', 'A')
+
+  sbox.simple_move('A/A/A', 'AAA_2')
+
+  sbox.simple_rm('A/A')
+  sbox.simple_move('C/A', 'A/A')
+
+  sbox.simple_move('A/A/A', 'AAA_3')
+
+def move_many_status(wc_dir):
+  "obtain standard status after setup_move_many"
+
+  return svntest.wc.State(wc_dir, {
+      ''                  : Item(status='  ', wc_rev='2'),
+
+      'AAA_1'             : Item(status='A ', copied='+', moved_from='A/A/A', wc_rev='-'),
+      'AAA_1/A'           : Item(status='  ', copied='+', wc_rev='-'),
+
+      'AAA_2'             : Item(status='A ', copied='+', moved_from='A/A/A', wc_rev='-'),
+      'AAA_2/A'           : Item(status='  ', copied='+', wc_rev='-'),
+
+      'AAA_3'             : Item(status='A ', copied='+', moved_from='A/A/A', wc_rev='-'),
+      'AAA_3/A'           : Item(status='  ', copied='+', wc_rev='-'),
+
+      'A'                 : Item(status='R ', copied='+', moved_from='B', wc_rev='-'),
+      'A/A'               : Item(status='R ', copied='+', moved_from='C/A', wc_rev='-'),
+      'A/A/A'             : Item(status='D ', copied='+', wc_rev='-', moved_to='AAA_3'),
+      'A/A/A/A'           : Item(status='D ', copied='+', wc_rev='-'),
+
+      'B'                 : Item(status='D ', wc_rev='2', moved_to='A'),
+      'B/A'               : Item(status='D ', wc_rev='2'),
+      'B/A/A'             : Item(status='D ', wc_rev='2'),
+      'B/A/A/A'           : Item(status='D ', wc_rev='2'),
+
+      'C'                 : Item(status='  ', wc_rev='2'),
+      'C/A'               : Item(status='D ', wc_rev='2', moved_to='A/A'),
+      'C/A/A'             : Item(status='D ', wc_rev='2'),
+      'C/A/A/A'           : Item(status='D ', wc_rev='2'),
+    })
+
+def move_many_update_delete(sbox):
+  "move many and delete-on-update"
+
+  sbox.build()
+  setup_move_many(sbox)
+
+  wc_dir = sbox.wc_dir
+
+  # Verify start situation
+  expected_status = move_many_status(wc_dir)
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+  # And now create a tree conflict
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'rm', sbox.repo_url + '/B',
+                                     '-m', '')
+
+  expected_output = svntest.wc.State(wc_dir, {
+     'B'                 : Item(status='  ', treeconflict='C'),
+    })
+
+
+  expected_status.tweak('', 'C', 'C/A', 'C/A/A', 'C/A/A/A', wc_rev='3')
+  expected_status.tweak('A', moved_from=None)
+  expected_status.remove('B/A', 'B/A/A', 'B/A/A/A')
+  expected_status.tweak('B', status='! ', treeconflict='C', wc_rev=None, moved_to=None)
+
+  svntest.actions.run_and_verify_update(wc_dir, expected_output, None,
+                                        expected_status)
+
+  # Would be nice if we could run the resolver as a separate step, 
+  # but 'svn resolve' just fails for any value but working
+
+@XFail()
+def move_many_update_add(sbox):
+  "move many and add-on-update"
+
+  sbox.build()
+  setup_move_many(sbox)
+
+  wc_dir = sbox.wc_dir
+
+  # Verify start situation
+  expected_status = move_many_status(wc_dir)
+  #svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+  # And now create a tree conflict
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'mkdir', sbox.repo_url + '/B/A/A/BB',
+                                     '-m', '')
+
+  expected_output = svntest.wc.State(wc_dir, {
+     'B'                 : Item(status='  ', treeconflict='C'),
+     'B/A'               : Item(status='  ', treeconflict='U'),
+     'B/A/A'             : Item(status='  ', treeconflict='U'),
+     'B/A/A/BB'          : Item(status='  ', treeconflict='A'),
+     # And while resolving
+     'A/A/'              : Item(status='  ', treeconflict='C')
+    })
+
+  expected_status.tweak('',
+                        'B', 'B/A', 'B/A/A', 'B/A/A/A',
+                        'C', 'C/A', 'C/A/A', 'C/A/A/A',
+                        wc_rev='3')
+
+  expected_status.tweak('A/A', treeconflict='C')
+  expected_status.add({
+        'A/A/A/BB'          : Item(status='D ', copied='+', wc_rev='-'),
+        'B/A/A/BB'          : Item(status='D ', wc_rev='3'),
+    })
+
+  svntest.actions.run_and_verify_update(wc_dir, expected_output, None,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, None,
+                                        wc_dir, '--accept', 'mine-conflict')
+
+  # And another one
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'mkdir', sbox.repo_url + '/C/A/A/BB',
+                                     '-m', '')
+
+  expected_status.tweak('',
+                        'B', 'B/A', 'B/A/A', 'B/A/A/A',
+                        'C', 'C/A', 'C/A/A', 'C/A/A/A',
+                        wc_rev='4')
+
+  expected_output = svntest.wc.State(wc_dir, {
+     'C/A'               : Item(status='  ', treeconflict='C'),
+     'C/A/A'             : Item(status='  ', treeconflict='U'),
+     'C/A/A/BB'          : Item(status='  ', treeconflict='A'),
+    })
+
+  # This currently triggers an assertion failure
+  svntest.actions.run_and_verify_update(wc_dir, expected_output, None,
+                                        expected_status,
+                                        None, None, None,
+                                        None, None, None,
+                                        wc_dir, '--accept', 'mine-conflict')
+
+
 #######################################################################
 # Run the tests
 
@@ -1245,6 +1408,8 @@ test_list = [ None,
               deeper_move_file_test,
               property_merge,
               move_missing,
+              move_many_update_delete,
+              move_many_update_add,
             ]
 
 if __name__ == '__main__':

Modified: subversion/branches/1.8.x/subversion/tests/cmdline/revert_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/tests/cmdline/revert_tests.py?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/tests/cmdline/revert_tests.py (original)
+++ subversion/branches/1.8.x/subversion/tests/cmdline/revert_tests.py Tue Oct 22 17:01:42 2013
@@ -1644,6 +1644,8 @@ def revert_obstructing_wc(sbox):
                                      'revert', '-R', wc_dir)
 
 
+
+
 ########################################################################
 # Run the tests
 
@@ -1683,7 +1685,7 @@ test_list = [ None,
               revert_no_text_change_conflict_recursive,
               revert_with_unversioned_targets,
               revert_nonexistent,
-              revert_obstructing_wc
+              revert_obstructing_wc,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/1.8.x/subversion/tests/libsvn_wc/op-depth-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.8.x/subversion/tests/libsvn_wc/op-depth-test.c?rev=1534703&r1=1534702&r2=1534703&view=diff
==============================================================================
--- subversion/branches/1.8.x/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/branches/1.8.x/subversion/tests/libsvn_wc/op-depth-test.c Tue Oct 22 17:01:42 2013
@@ -8149,6 +8149,197 @@ update_with_tree_conflict(const svn_test
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+move_child_to_parent_revert(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "move_child_to_parent_revert", opts,
+                                   pool));
+
+  SVN_ERR(sbox_wc_mkdir(&b, "A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/B"));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+
+
+  SVN_ERR(sbox_wc_move(&b, "A/B", "B"));
+  SVN_ERR(sbox_wc_delete(&b, "A"));
+
+  /* Verify that the move is still recorded correctly */
+  {
+    nodes_row_t nodes[] = {
+      {0, "",    "normal", 0, ""},
+      {0, "A",   "normal", 1, "A"},
+      {0, "A/B", "normal", 1, "A/B"},
+
+      {1, "A",   "base-deleted", NO_COPY_FROM},
+      {1, "A/B", "base-deleted", NO_COPY_FROM, "B"},
+
+      {1, "B", "normal", 1, "A/B", MOVED_HERE},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_infinity));
+
+  /* Verify that the move is now just a copy */
+  {
+    nodes_row_t nodes[] = {
+      {0, "",    "normal", 0, ""},
+      {0, "A",   "normal", 1, "A"},
+      {0, "A/B", "normal", 1, "A/B"},
+
+      {1, "B", "normal", 1, "A/B"},
+      {0}
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+move_abspath_more_than_once(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "move_child_to_parent_revert", opts,
+                                   pool));
+
+  SVN_ERR(sbox_wc_mkdir(&b, "A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "B"));
+  SVN_ERR(sbox_wc_mkdir(&b, "B/A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "B/A/A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "B/A/A/A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "C"));
+  SVN_ERR(sbox_wc_mkdir(&b, "C/A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "C/A/A"));
+  SVN_ERR(sbox_wc_mkdir(&b, "C/A/A/A"));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+
+  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_1"));
+
+  SVN_ERR(sbox_wc_delete(&b, "A"));
+  SVN_ERR(sbox_wc_move(&b, "B", "A"));
+
+  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_2"));
+
+  SVN_ERR(sbox_wc_delete(&b, "A/A"));
+  SVN_ERR(sbox_wc_move(&b, "C/A", "A/A"));
+
+  SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_3"));
+
+  /* Verify that the move is still recorded correctly */
+  {
+    nodes_row_t nodes[] = {
+
+      {0, "",           "normal",       0, ""},
+
+      {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},
+
+      {0, "A",          "normal",       1, "A"},
+      {0, "A/A",        "normal",       1, "A/A"},
+      {0, "A/A/A",      "normal",       1, "A/A/A"},
+      {0, "A/A/A/A",    "normal",       1, "A/A/A/A"},
+
+      {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",           MOVED_HERE},
+
+      {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",           MOVED_HERE},
+
+      {3, "A/A/A",      "base-deleted", NO_COPY_FROM,      "AAA_3"},
+      {3, "A/A/A/A",    "base-deleted", NO_COPY_FROM},
+
+      {0, "B",          "normal",       1, "B"},
+      {0, "B/A",        "normal",       1, "B/A"},
+      {0, "B/A/A",      "normal",       1, "B/A/A"},
+      {0, "B/A/A/A",    "normal",       1, "B/A/A/A"},
+
+      {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},
+
+      {0, "C",          "normal",       1, "C"},
+      {0, "C/A",        "normal",       1, "C/A"},
+      {0, "C/A/A",      "normal",       1, "C/A/A"},
+      {0, "C/A/A/A",    "normal",       1, "C/A/A/A"},
+
+      {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},
+
+      {0},
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  /* Ok, now we are in the very ugly case where A/A/A is moved away 3 times */
+
+  /* Let's revert A */
+  SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_infinity));
+
+  /* AAA_1 should now be a copy, but AAA_2 and AAA_3 should still be moves,
+     but now from the original location instead of from "A/A/A" */
+  {
+    nodes_row_t nodes[] = {
+
+      {0, "",           "normal",       0, ""},
+
+      {1, "AAA_1",      "normal",       1, "A/A/A",},
+      {1, "AAA_1/A",    "normal",       1, "A/A/A/A"},
+      {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},
+
+      {0, "A",          "normal",       1, "A"},
+      {0, "A/A",        "normal",       1, "A/A"},
+      {0, "A/A/A",      "normal",       1, "A/A/A"},
+      {0, "A/A/A/A",    "normal",       1, "A/A/A/A"},
+
+      {0, "B",          "normal",       1, "B"},
+      {0, "B/A",        "normal",       1, "B/A"},
+      {0, "B/A/A",      "normal",       1, "B/A/A"},
+      {0, "B/A/A/A",    "normal",       1, "B/A/A/A"},
+
+      {1, "B",          "base-deleted", NO_COPY_FROM},
+      {1, "B/A",        "base-deleted", NO_COPY_FROM},
+      {1, "B/A/A",      "base-deleted", NO_COPY_FROM, "AAA_2"},
+      {1, "B/A/A/A",    "base-deleted", NO_COPY_FROM},
+
+      {0, "C",          "normal",       1, "C"},
+      {0, "C/A",        "normal",       1, "C/A"},
+      {0, "C/A/A",      "normal",       1, "C/A/A"},
+      {0, "C/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, "AAA_3"},
+      {2, "C/A/A/A",    "base-deleted", NO_COPY_FROM},
+
+      {0},
+    };
+    SVN_ERR(check_db_rows(&b, "", nodes));
+  }
+
+  return SVN_NO_ERROR;
+}
+
+
 /* ---------------------------------------------------------------------- */
 /* The list of test functions */
 
@@ -8302,5 +8493,9 @@ struct svn_test_descriptor_t test_funcs[
                        "move/delete file externals (issue 4293)"),
     SVN_TEST_OPTS_PASS(update_with_tree_conflict,
                        "update with tree conflict (issue 4347)"),
+    SVN_TEST_OPTS_PASS(move_child_to_parent_revert,
+                       "move child to parent and revert (issue 4436)"),
+    SVN_TEST_OPTS_XFAIL(move_abspath_more_than_once,
+                       "move one abspath more than once"),
     SVN_TEST_NULL
   };