Index: subversion/libsvn_wc/copy.c
===================================================================
--- subversion/libsvn_wc/copy.c	(revision 20721)
+++ subversion/libsvn_wc/copy.c	(working copy)
@@ -39,6 +39,70 @@
 
 /*** Code. ***/
 
+/* Helper function for copy_file_administratively() and
+   copy_dir_administratively().  Determines the COPYFROM_URL and
+   COPYFROM_REV of a file or directory SRC_PATH which is the descendant
+   of an explicitly moved or copied directory that has not been committed.
+*/
+static svn_error_t *
+get_copyfrom_url_rev_via_parent(const char *src_path,
+                                const char **copyfrom_url,
+                                svn_revnum_t *copyfrom_rev,
+                                svn_wc_adm_access_t *src_access,
+                                apr_pool_t *pool)
+{
+  const char *parent_path = svn_path_dirname(src_path, pool);
+  const char *rest = svn_path_basename(src_path, pool);
+  *copyfrom_url = NULL;
+
+  while (! *copyfrom_url)
+    {
+      svn_wc_adm_access_t *parent_access;
+      const svn_wc_entry_t *entry;
+
+      /* Don't look for parent_path in src_access if it can't be
+         there... */
+      if (svn_path_is_ancestor(svn_wc_adm_access_path(src_access),
+                               parent_path))
+        {
+          SVN_ERR(svn_wc_adm_retrieve(&parent_access, src_access,
+                                      parent_path, pool));
+          SVN_ERR(svn_wc_entry(&entry, parent_path, parent_access,
+                               FALSE, pool));
+        }
+      else /* ...get access for parent_path instead. */
+        {
+          SVN_ERR(svn_wc_adm_probe_open3(&parent_access, NULL,
+                                         parent_path, FALSE, -1,
+                                         NULL, NULL, pool));
+          SVN_ERR(svn_wc_entry(&entry, parent_path, parent_access,
+                               FALSE, pool));
+          SVN_ERR(svn_wc_adm_close(parent_access));
+        }
+
+      if (! entry)
+        return svn_error_createf
+          (SVN_ERR_ENTRY_NOT_FOUND, NULL,
+           _("'%s' is not under version control"),
+             svn_path_local_style(parent_path, pool));
+
+      if (entry->copyfrom_url)
+        {
+          *copyfrom_url = svn_path_join(entry->copyfrom_url, rest,
+                                        pool);
+          *copyfrom_rev = entry->copyfrom_rev;
+        }
+      else
+        {
+          rest = svn_path_join(svn_path_basename(parent_path, pool),
+                               rest, pool);
+          parent_path = svn_path_dirname(parent_path, pool);
+        }
+    }
+
+  return SVN_NO_ERROR;
+} 
+
 /* This function effectively creates and schedules a file for
    addition, but does extra administrative things to allow it to
    function as a 'copy'.
@@ -90,18 +154,19 @@
                                  svn_path_local_style(dst_path, pool));
     }
 
-  /* Sanity check:  you cannot make a copy of something that's not
-     in the repository.  See comment at the bottom of this file for an
-     explanation. */
+  /* Sanity check 1: You cannot make a copy of something that's not
+     under version control. */
   SVN_ERR(svn_wc_entry(&src_entry, src_path, src_access, FALSE, pool));
   if (! src_entry)
     return svn_error_createf 
       (SVN_ERR_UNVERSIONED_RESOURCE, NULL,
        _("Cannot copy or move '%s': it's not under version control"),
        svn_path_local_style(src_path, pool));
-  if ((src_entry->schedule == svn_wc_schedule_add)
-      || (! src_entry->url)
-      || (src_entry->copied))
+
+  /* Sanity check 2: You cannot make a copy of something that's not
+     in the repository unless it's a copy of an uncommitted copy. */
+  if ((src_entry->schedule == svn_wc_schedule_add && (! src_entry->copied))
+      || (! src_entry->url))
     return svn_error_createf 
       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
        _("Cannot copy or move '%s': it's not in the repository yet; "
@@ -111,14 +176,43 @@
 
   /* Schedule the new file for addition in its parent, WITH HISTORY. */
   {
-    char *copyfrom_url;
+    const char *copyfrom_url;
     const char *tmp_wc_text;
     svn_revnum_t copyfrom_rev;
     apr_hash_t *props, *base_props;
 
-    SVN_ERR(svn_wc_get_ancestry(&copyfrom_url, &copyfrom_rev,
-                                src_path, src_access, pool));
+    /* Are we moving or copying a file that is already moved or copied
+       but not committed? */
+    if (src_entry->copied)
+      {
+        if (src_entry->copyfrom_url)
+          {
+            /* When copying/moving a file that was already explicitly
+               copied/moved then we know the URL it was copied from... */
+            copyfrom_url = apr_pstrdup(pool, src_entry->copyfrom_url);
+            copyfrom_rev = src_entry->copyfrom_rev;
+          }
+        else
+          {
+            /* ...But if this file is merely the descendant of an explicitly
+               copied/moved directory, we need to do a bit more work to
+               determine copyfrom_url and copyfrom_rev. */
+            SVN_ERR(get_copyfrom_url_rev_via_parent(src_path, &copyfrom_url,
+                                                    &copyfrom_rev,
+                                                    src_access, pool));
+          }
+      }
+    else
+      {
+        /* Grrr.  Why isn't the first arg to svn_wc_get_ancestry const? */
+        char *tmp;
 
+        SVN_ERR(svn_wc_get_ancestry(&tmp, &copyfrom_rev, src_path, src_access,
+                                    pool));
+
+        copyfrom_url = tmp;
+      }
+
     /* Load source base and working props. */
     SVN_ERR(svn_wc__load_props(&base_props, &props, src_access,
                                src_entry->name, pool));
@@ -339,18 +433,19 @@
   const char *dst_path = svn_path_join(svn_wc_adm_access_path(dst_parent),
                                        dst_basename, pool);
 
-  /* Sanity check:  you cannot make a copy of something that's not
-     in the repository.  See comment at the bottom of this file for an
-     explanation. */
+  /* Sanity check 1: You cannot make a copy of something that's not
+     under version control. */
   SVN_ERR(svn_wc_entry(&src_entry, src_path, src_access, FALSE, pool));
   if (! src_entry)
     return svn_error_createf
       (SVN_ERR_ENTRY_NOT_FOUND, NULL, 
        _("'%s' is not under version control"),
        svn_path_local_style(src_path, pool));
-  if ((src_entry->schedule == svn_wc_schedule_add)
-      || (! src_entry->url)
-      || (src_entry->copied))
+
+  /* Sanity check 2: You cannot make a copy of something that's not
+     in the repository unless it's a copy of an uncommitted copy. */
+  if ((src_entry->schedule == svn_wc_schedule_add && (! src_entry->copied))
+      || (! src_entry->url))
     return svn_error_createf 
       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
        _("Cannot copy or move '%s': it is not in the repository yet; "
@@ -379,18 +474,57 @@
   SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, dst_path, TRUE, -1,
                            cancel_func, cancel_baton, pool));
   SVN_ERR(post_copy_cleanup(adm_access, pool));
-  SVN_ERR(svn_wc_adm_close(adm_access));
 
   /* Schedule the directory for addition in both its parent and itself
      (this_dir) -- WITH HISTORY.  This function should leave the
      existing administrative dir untouched.  */
   {
-    char *copyfrom_url;
+    const char *copyfrom_url;
     svn_revnum_t copyfrom_rev;
-    
-    SVN_ERR(svn_wc_get_ancestry(&copyfrom_url, &copyfrom_rev,
-                                src_path, src_access, pool));
-    
+    svn_wc_entry_t tmp_entry;
+
+    /* Are we copying a dir that is already copied but not committed? */
+    if (src_entry->copied)
+      {
+        if (src_entry->copyfrom_url)
+          {
+            /* When copying/moving a dir that was already explicitly
+               copied/moved then we know the URL it was copied from... */
+            copyfrom_url = apr_pstrdup(pool, src_entry->copyfrom_url);
+            copyfrom_rev = src_entry->copyfrom_rev;
+          }
+        else
+          {
+            /* ...But if this dir is merely the descendant of an explicitly
+               copied/moved directory, we need to do a bit more work to
+               determine copyfrom_url and copyfrom_rev. */
+            SVN_ERR(get_copyfrom_url_rev_via_parent(src_path, &copyfrom_url,
+                                                    &copyfrom_rev,
+                                                    src_access, pool));
+          }
+
+        /* The URL for a copied dir won't exist in the repository, which
+           will cause  svn_wc_add2() below to fail.  Set the URL to the
+           URL of the first copy for now to prevent this. */
+        tmp_entry.url = apr_pstrdup(pool, copyfrom_url);
+        SVN_ERR(svn_wc__entry_modify(adm_access, NULL, /* This Dir */
+                                     &tmp_entry,
+                                     SVN_WC__ENTRY_MODIFY_URL, TRUE,
+                                     pool));
+      }
+    else
+      {
+        /* Grrr.  Why isn't the first arg to svn_wc_get_ancestry const? */
+        char *tmp;
+
+        SVN_ERR(svn_wc_get_ancestry(&tmp, &copyfrom_rev, src_path, src_access,
+                                    pool));
+
+        copyfrom_url = tmp;
+      }
+
+    SVN_ERR(svn_wc_adm_close(adm_access));
+
     SVN_ERR(svn_wc_add2(dst_path, dst_parent,
                         copyfrom_url, copyfrom_rev,
                         cancel_func, cancel_baton,
@@ -492,29 +626,3 @@
 }
 
 
-/*
-  Rabbinic Commentary
-
-
-  Q:  Why can't we 'svn cp' something that we just copied?
-      i.e.  'svn cp foo foo2;  svn cp foo2 foo3"
-
-  A:  It leads to inconsistencies.
-
-      In the example above, foo2 has no associated repository URL,
-      because it hasn't been committed yet.  But suppose foo3 simply
-      inherited foo's URL (i.e. foo3 'pointed' to foo as a copy
-      ancestor by virtue of transitivity.)
- 
-      For one, this is not what the user would expect.  That's
-      certainly not what the user typed!  Second, suppose that the
-      user did a commit between the two 'svn cp' commands.  Now foo3
-      really *would* point to foo2, but without that commit, it
-      pointed to foo.  Ugly inconsistency, and the user has no idea
-      that foo3's ancestor would be different in each case.
-
-      And even if somehow we *could* make foo3 point to foo2 before
-      foo2 existed in the repository... what's to prevent a user from
-      committing foo3 first?  That would break.
-
-*/
Index: subversion/tests/cmdline/copy_tests.py
===================================================================
--- subversion/tests/cmdline/copy_tests.py	(revision 20721)
+++ subversion/tests/cmdline/copy_tests.py	(working copy)
@@ -1885,6 +1885,552 @@
                                      'cat',
                                      svntest.main.current_repo_url + '/dest')
 
+
+def copy_copied_file_and_dir(sbox):
+  "copy a copied file and dir"
+  # Improve support for copy and move
+  # Allow copy of copied items without a commit between
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+  rho_copy_path_1 = os.path.join(wc_dir, 'A', 'D', 'rho_copy_1')
+  rho_copy_path_2 = os.path.join(wc_dir, 'A', 'B', 'F', 'rho_copy_2')
+
+  # Copy A/D/G/rho to A/D/rho_copy_1
+  svntest.actions.run_and_verify_svn(None, None, [], 'cp',
+                                     rho_path, rho_copy_path_1)
+
+  # Copy the copied file: A/D/rho_copy_1 to A/B/F/rho_copy_2
+  svntest.actions.run_and_verify_svn(None, None, [], 'cp',
+                                     rho_copy_path_1, rho_copy_path_2)
+
+  E_path = os.path.join(wc_dir, 'A', 'B', 'E')
+  E_path_copy_1 = os.path.join(wc_dir, 'A', 'B', 'F', 'E_copy_1')
+  E_path_copy_2 = os.path.join(wc_dir, 'A', 'D', 'G', 'E_copy_2')
+
+  # Copy A/B/E to A/B/F/E_copy_1
+  svntest.actions.run_and_verify_svn(None, None, [], 'cp',
+                                     E_path, E_path_copy_1)
+
+  # Copy the copied dir: A/B/F/E_copy_1 to A/D/G/E_copy_2
+  svntest.actions.run_and_verify_svn(None, None, [], 'cp',
+                                     E_path_copy_1, E_path_copy_2)
+
+  # Created expected output tree for 'svn ci':
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/D/rho_copy_1'       : Item(verb='Adding'),
+    'A/B/F/rho_copy_2'     : Item(verb='Adding'),
+    'A/B/F/E_copy_1/'      : Item(verb='Adding'),
+    'A/D/G/E_copy_2/'      : Item(verb='Adding'),
+    })
+
+  # Create expected status tree 
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak(wc_rev=1)
+  expected_status.add({
+    'A/D/rho_copy_1'       : Item(status='  ', wc_rev=2),
+    'A/B/F/rho_copy_2'     : Item(status='  ', wc_rev=2),
+    'A/B/F/E_copy_1'       : Item(status='  ', wc_rev=2),
+    'A/B/F/E_copy_1/alpha' : Item(status='  ', wc_rev=2),
+    'A/B/F/E_copy_1/beta'  : Item(status='  ', wc_rev=2),
+    'A/D/G/E_copy_2'       : Item(status='  ', wc_rev=2),
+    'A/D/G/E_copy_2/alpha' : Item(status='  ', wc_rev=2),
+    'A/D/G/E_copy_2/beta'  : Item(status='  ', wc_rev=2),
+    })
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        None, None,
+                                        None, None,
+                                        wc_dir)
+
+
+def move_copied_file_and_dir(sbox):
+  "move a copied file and dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+  rho_copy_path = os.path.join(wc_dir, 'A', 'D', 'rho_copy')
+  rho_copy_move_path = os.path.join(wc_dir, 'A', 'B', 'F', 'rho_copy_moved')
+
+  # Copy A/D/G/rho to A/D/rho_copy
+  svntest.actions.run_and_verify_svn(None, None, [], 'cp',
+                                     rho_path, rho_copy_path)
+
+  # Move the copied file: A/D/rho_copy to A/B/F/rho_copy_moved
+  #
+  # The --force option is required we get this error without it:
+  #
+  #   svn: Use --force to override this restriction
+  #   svn: Move will not be attempted unless forced
+  #   svn: 'svn-test-work\working_copies\copy_tests-1\A\D\rho_copy_1'
+  #        has local modifications
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     rho_copy_path, rho_copy_move_path,
+                                     '--force')
+
+  E_path = os.path.join(wc_dir, 'A', 'B', 'E')
+  E_path_copy = os.path.join(wc_dir, 'A', 'B', 'F', 'E_copy')
+  E_path_copy_move = os.path.join(wc_dir, 'A', 'D', 'G', 'E_copy_moved')
+
+  # Copy A/B/E to A/B/F/E_copy
+  svntest.actions.run_and_verify_svn(None, None, [], 'cp',
+                                     E_path, E_path_copy)
+
+  # Move the copied file: A/B/F/E_copy to A/D/G/E_copy_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     E_path_copy, E_path_copy_move,
+                                     '--force')
+
+  # Created expected output tree for 'svn ci':
+  # Since we are moving items that were only *scheduled* for addition
+  # we expect only to additions when checking in, rather than a
+  # deletion/addition pair.
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/F/rho_copy_moved' : Item(verb='Adding'),
+    'A/D/G/E_copy_moved/'  : Item(verb='Adding'),
+    })
+
+  # Create expected status tree 
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak(wc_rev=1)
+  expected_status.add({
+    'A/B/F/rho_copy_moved'     : Item(status='  ', wc_rev=2),
+    'A/D/G/E_copy_moved'       : Item(status='  ', wc_rev=2),
+    'A/D/G/E_copy_moved/alpha' : Item(status='  ', wc_rev=2),
+    'A/D/G/E_copy_moved/beta'  : Item(status='  ', wc_rev=2),
+    })
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        None, None,
+                                        None, None,
+                                        wc_dir)
+
+
+def move_moved_file_and_dir(sbox):
+  "move a moved file and dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+  rho_move_path = os.path.join(wc_dir, 'A', 'D', 'rho_moved')
+  rho_move_moved_path = os.path.join(wc_dir, 'A', 'B', 'F', 'rho_move_moved')
+
+  # Move A/D/G/rho to A/D/rho_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     rho_path, rho_move_path)
+
+  # Move the moved file: A/D/rho_moved to A/B/F/rho_move_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     rho_move_path, rho_move_moved_path,
+                                     '--force')
+
+  E_path = os.path.join(wc_dir, 'A', 'B', 'E')
+  E_path_moved = os.path.join(wc_dir, 'A', 'B', 'F', 'E_moved')
+  E_path_move_moved = os.path.join(wc_dir, 'A', 'D', 'G', 'E_move_moved')
+
+  # Copy A/B/E to A/B/F/E_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     E_path, E_path_moved)
+
+  # Move the moved file: A/B/F/E_moved to A/D/G/E_move_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     E_path_moved, E_path_move_moved,
+                                     '--force')
+
+  # Created expected output tree for 'svn ci':
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/E'                : Item(verb='Deleting'),
+    'A/D/G/E_move_moved/'  : Item(verb='Adding'),
+    'A/D/G/rho'            : Item(verb='Deleting'),
+    'A/B/F/rho_move_moved' : Item(verb='Adding'),
+    })
+
+  # Create expected status tree 
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak(wc_rev=1)
+  expected_status.add({
+    'A/D/G/E_move_moved/'      : Item(status='  ', wc_rev=2),
+    'A/D/G/E_move_moved/alpha' : Item(status='  ', wc_rev=2),
+    'A/D/G/E_move_moved/beta'  : Item(status='  ', wc_rev=2),
+    'A/B/F/rho_move_moved'     : Item(status='  ', wc_rev=2),
+    })
+
+  expected_status.remove('A/B/E',
+                         'A/B/E/alpha',
+                         'A/B/E/beta',
+                         'A/D/G/rho')
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        None, None,
+                                        None, None,
+                                        wc_dir)
+
+
+def move_file_within_moved_dir(sbox):
+  "move a file twice within a moved dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  D_path = os.path.join(wc_dir, 'A', 'D')
+  D_path_moved = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved')
+
+  # Move A/B/D to A/B/F/D_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     D_path, D_path_moved)
+
+  chi_path = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved', 'H', 'chi')
+  chi_moved_path = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved',
+                                'H', 'chi_moved')
+  chi_moved_again_path = os.path.join(wc_dir, 'A', 'B', 'F',
+                                      'D_moved', 'H', 'chi_moved_again')
+
+  # Move A/B/F/D_moved/H/chi to A/B/F/D_moved/H/chi_moved
+  # then move that to A/B/F/D_moved/H/chi_moved_again
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     chi_path, chi_moved_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     chi_moved_path,
+                                     chi_moved_again_path,
+                                     '--force')
+
+  # Created expected output tree for 'svn ci':
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/F/D_moved/'                  : Item(verb='Adding'),
+    'A/B/F/D_moved/H/chi'             : Item(verb='Deleting'),
+    'A/B/F/D_moved/H/chi_moved_again' : Item(verb='Adding'),
+    'A/D'                             : Item(verb='Deleting'),
+    })
+
+  # Create expected status tree 
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak(wc_rev=1)
+  expected_status.add({
+    'A/B/F/D_moved'                   : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/gamma'             : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G'                 : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/pi'              : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/rho'             : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/tau'             : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H'                 : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H/omega'           : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H/psi'             : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H/chi_moved_again' : Item(status='  ', wc_rev=2),
+    })
+
+  expected_status.remove('A/D',
+                         'A/D/gamma',
+                         'A/D/G',
+                         'A/D/G/pi',
+                         'A/D/G/rho',
+                         'A/D/G/tau',
+                         'A/D/H',
+                         'A/D/H/chi',
+                         'A/D/H/omega',
+                         'A/D/H/psi',
+                         )
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        None, None,
+                                        None, None,
+                                        wc_dir)
+
+
+def move_file_out_of_moved_dir(sbox):
+  "move a file out of a moved dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  D_path = os.path.join(wc_dir, 'A', 'D')
+  D_path_moved = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved')
+
+  # Move A/B/D to A/B/F/D_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     D_path, D_path_moved)
+
+  chi_path = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved', 'H', 'chi')
+  chi_moved_path = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved',
+                                'H', 'chi_moved')
+  chi_moved_again_path = os.path.join(wc_dir, 'A', 'C', 'chi_moved_again')
+
+  # Move A/B/F/D_moved/H/chi to A/B/F/D_moved/H/chi_moved
+  # then move that to A/C/chi_moved_again
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     chi_path, chi_moved_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     chi_moved_path,
+                                     chi_moved_again_path,
+                                     '--force')
+
+  # Created expected output tree for 'svn ci':
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/F/D_moved/'      : Item(verb='Adding'),
+    'A/B/F/D_moved/H/chi' : Item(verb='Deleting'),
+    'A/C/chi_moved_again' : Item(verb='Adding'),
+    'A/D'                 : Item(verb='Deleting'),
+    })
+
+  # Create expected status tree 
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak(wc_rev=1)
+  expected_status.add({
+    'A/B/F/D_moved'         : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/gamma'   : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G'       : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/pi'    : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/rho'   : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/tau'   : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H'       : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H/omega' : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H/psi'   : Item(status='  ', wc_rev=2),
+    'A/C/chi_moved_again'   : Item(status='  ', wc_rev=2),
+    })
+
+  expected_status.remove('A/D',
+                         'A/D/gamma',
+                         'A/D/G',
+                         'A/D/G/pi',
+                         'A/D/G/rho',
+                         'A/D/G/tau',
+                         'A/D/H',
+                         'A/D/H/chi',
+                         'A/D/H/omega',
+                         'A/D/H/psi',
+                         )
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        None, None,
+                                        None, None,
+                                        wc_dir)
+
+
+def move_dir_within_moved_dir(sbox):
+  "move a dir twice within a moved dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  D_path = os.path.join(wc_dir, 'A', 'D')
+  D_path_moved = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved')
+
+  # Move A/D to A/B/F/D_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     D_path, D_path_moved)
+
+  H_path = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved', 'H')
+  H_moved_path = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved', 'H_moved')
+  H_moved_again_path = os.path.join(wc_dir, 'A', 'B', 'F',
+                                    'D_moved', 'H_moved_again')
+
+  # Move A/B/F/D_moved/H to A/B/F/D_moved/H_moved
+  # then move that to A/B/F/D_moved/H_moved_again
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     H_path, H_moved_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     H_moved_path,
+                                     H_moved_again_path,
+                                     '--force')
+
+  # Created expected output tree for 'svn ci':
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/D'                         : Item(verb='Deleting'),
+    'A/B/F/D_moved'               : Item(verb='Adding'),
+    'A/B/F/D_moved/H'             : Item(verb='Deleting'),
+    'A/B/F/D_moved/H_moved_again' : Item(verb='Adding'),
+    })
+
+  # Create expected status tree 
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak(wc_rev=1)
+  expected_status.add({
+    'A/B/F/D_moved'                     : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/gamma'               : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G'                   : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/pi'                : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/rho'               : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/tau'               : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H_moved_again'       : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H_moved_again/omega' : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H_moved_again/psi'   : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/H_moved_again/chi'   : Item(status='  ', wc_rev=2),
+    })
+
+  expected_status.remove('A/D',
+                         'A/D/gamma',
+                         'A/D/G',
+                         'A/D/G/pi',
+                         'A/D/G/rho',
+                         'A/D/G/tau',
+                         'A/D/H',
+                         'A/D/H/chi',
+                         'A/D/H/omega',
+                         'A/D/H/psi',
+                         )
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        None, None,
+                                        None, None,
+                                        wc_dir)
+
+
+def move_dir_out_of_moved_dir(sbox):
+  "move a dir out of a moved dir"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  D_path = os.path.join(wc_dir, 'A', 'D')
+  D_path_moved = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved')
+
+  # Move A/D to A/B/F/D_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     D_path, D_path_moved)
+
+  H_path = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved', 'H')
+  H_moved_path = os.path.join(wc_dir, 'A', 'B', 'F', 'D_moved', 'H_moved')
+  H_moved_again_path = os.path.join(wc_dir, 'A', 'C', 'H_moved_again')
+
+  # Move A/B/F/D_moved/H to A/B/F/D_moved/H_moved
+  # then move that to A/C/H_moved_again
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     H_path, H_moved_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     H_moved_path,
+                                     H_moved_again_path,
+                                     '--force')
+
+  # Created expected output tree for 'svn ci':
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/D'               : Item(verb='Deleting'),
+    'A/B/F/D_moved'     : Item(verb='Adding'),
+    'A/B/F/D_moved/H'   : Item(verb='Deleting'),
+    'A/C/H_moved_again' : Item(verb='Adding'),
+    })
+
+  # Create expected status tree 
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak(wc_rev=1)
+  expected_status.add({
+    'A/B/F/D_moved'           : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/gamma'     : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G'         : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/pi'      : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/rho'     : Item(status='  ', wc_rev=2),
+    'A/B/F/D_moved/G/tau'     : Item(status='  ', wc_rev=2),
+    'A/C/H_moved_again'       : Item(status='  ', wc_rev=2),
+    'A/C/H_moved_again/omega' : Item(status='  ', wc_rev=2),
+    'A/C/H_moved_again/psi'   : Item(status='  ', wc_rev=2),
+    'A/C/H_moved_again/chi'   : Item(status='  ', wc_rev=2),
+    })
+
+  expected_status.remove('A/D',
+                         'A/D/gamma',
+                         'A/D/G',
+                         'A/D/G/pi',
+                         'A/D/G/rho',
+                         'A/D/G/tau',
+                         'A/D/H',
+                         'A/D/H/chi',
+                         'A/D/H/omega',
+                         'A/D/H/psi',
+                         )
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        None, None,
+                                        None, None,
+                                        wc_dir)
+
+def move_file_back_and_forth(sbox):
+  "move a moved file back to original location"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+  rho_move_path = os.path.join(wc_dir, 'A', 'D', 'rho_moved')
+
+  # Move A/D/G/rho to A/D/rho_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     rho_path, rho_move_path)
+
+  # Move the moved file: A/D/rho_moved to A/B/F/rho_move_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     rho_move_path, rho_path,
+                                     '--force')
+
+  # Created expected output tree for 'svn ci':
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/D/G/rho' : Item(verb='Replacing'),
+    })
+
+  # Create expected status tree 
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
+  expected_status.tweak(wc_rev=1)
+  expected_status.add({
+    'A/D/G/rho' : Item(status='  ', wc_rev=2),
+    })
+
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        None, None,
+                                        None, None,
+                                        wc_dir)
+
+
+def move_dir_back_and_forth(sbox):
+  "move a moved dir back to original location"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  D_path = os.path.join(wc_dir, 'A', 'D')
+  D_move_path = os.path.join(wc_dir, 'D_moved')
+
+  # Move A/D to D_moved
+  svntest.actions.run_and_verify_svn(None, None, [], 'mv',
+                                     D_path, D_move_path)
+
+  # Move the moved dir: D_moved back to it's starting
+  # location at A/D.
+  out, err = svntest.actions.run_and_verify_svn(None, None, SVNAnyOutput,
+                                                'mv', D_move_path,
+                                                D_path, '--force')
+
+  for line in err:
+    if re.match('.*Cannot copy to .*as it is scheduled for deletion',
+                line, ):
+      return
+  raise svntest.Failure("mv failed but not in the expected way")
+
 ########################################################################
 # Run the tests
 
@@ -1928,6 +2474,15 @@
               mv_unversioned_file,
               force_move,
               copy_deleted_dir_into_prefix,
+              copy_copied_file_and_dir,
+              move_copied_file_and_dir,
+              move_moved_file_and_dir,
+              move_file_within_moved_dir,
+              move_file_out_of_moved_dir,
+              move_dir_within_moved_dir,
+              move_dir_out_of_moved_dir,
+              move_file_back_and_forth,
+              move_dir_back_and_forth,
              ]
 
 if __name__ == '__main__':
