Author: rhuijben
Date: Tue Jan 22 21:53:37 2013
New Revision: 1437203

URL: http://svn.apache.org/viewvc?rev=1437203&view=rev
Log:
Resolve issue #3590, by making 'svn copy' capable of copying directory
structures from foreign repositories.

A notification is added to make it clear that this is not just a cheap
copy, like the intra repository copies.


* subversion/tests/libsvn_client/client-test.c
  (test_foreign_repos_copy): Update caller.

* subversion/tests/cmdline/copy_tests.py
  (repos_to_wc): Add more recent issue number. Update expected result for
    foreign repository copy.

* subversion/svn/notify.c
  (notify): Add new notification handling.

* subversion/libsvn_client/copy_foreign.c
  (edit_open): Make root directory when opening to make sure files in the root
    work properly when completely handled before other changes.
  (svn_client__copy_foreign): Allow usage within an existing lock.

* subversion/libsvn_client/copy.c
  (repos_to_wc_copy_single): Use svn_client__copy_foreign for foreign
    repository copies.

* subversion/include/svn_wc.h
  (svn_wc_notify_action_t): Add notification value.

* subversion/include/private/svn_client_private.h
  (svn_client__copy_foreign): Update prototype.

Modified:
    subversion/trunk/subversion/include/private/svn_client_private.h
    subversion/trunk/subversion/include/svn_wc.h
    subversion/trunk/subversion/libsvn_client/copy.c
    subversion/trunk/subversion/libsvn_client/copy_foreign.c
    subversion/trunk/subversion/svn/notify.c
    subversion/trunk/subversion/tests/cmdline/copy_tests.py
    subversion/trunk/subversion/tests/libsvn_client/client-test.c

Modified: subversion/trunk/subversion/include/private/svn_client_private.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_client_private.h?rev=1437203&r1=1437202&r2=1437203&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_client_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_client_private.h Tue Jan 22 
21:53:37 2013
@@ -255,6 +255,7 @@ svn_client__copy_foreign(const char *url
                          svn_opt_revision_t *revision,
                          svn_depth_t depth,
                          svn_boolean_t make_parents,
+                         svn_boolean_t already_locked,
                          svn_client_ctx_t *ctx,
                          apr_pool_t *scratch_pool);
 

Modified: subversion/trunk/subversion/include/svn_wc.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_wc.h?rev=1437203&r1=1437202&r2=1437203&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_wc.h (original)
+++ subversion/trunk/subversion/include/svn_wc.h Tue Jan 22 21:53:37 2013
@@ -1245,7 +1245,11 @@ typedef enum svn_wc_notify_action_t
   /** The current operation left local changes of something that was deleted
    * The changes are available on (and below) the notified path
    * @since New in 1.8. */
-  svn_wc_notify_left_local_modifications
+  svn_wc_notify_left_local_modifications,
+
+  /** A copy from a foreign repository has started 
+   * @since New in 1.8. */
+  svn_wc_notify_foreign_copy_begin
 
 } svn_wc_notify_action_t;
 

Modified: subversion/trunk/subversion/libsvn_client/copy.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/copy.c?rev=1437203&r1=1437202&r2=1437203&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/copy.c (original)
+++ subversion/trunk/subversion/libsvn_client/copy.c Tue Jan 22 21:53:37 2013
@@ -1548,56 +1548,56 @@ repos_to_wc_copy_single(svn_client__copy
 
   if (pair->src_kind == svn_node_dir)
     {
-      svn_boolean_t sleep_needed = FALSE;
-      const char *tmpdir_abspath, *tmp_abspath;
-
-      /* Find a temporary location in which to check out the copy source. */
-      SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, dst_abspath,
-                                 pool, pool));
-                                 
-      SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath,
-                                       svn_io_file_del_on_close, pool, pool));
-
-      /* Make a new checkout of the requested source. While doing so,
-       * resolve pair->src_revnum to an actual revision number in case it
-       * was until now 'invalid' meaning 'head'.  Ask this function not to
-       * sleep for timestamps, by passing a sleep_needed output param.
-       * Send notifications for all nodes except the root node, and adjust
-       * them to refer to the destination rather than this temporary path. */
-      {
-        svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2;
-        void *old_notify_baton2 = ctx->notify_baton2;
-        struct notification_adjust_baton nb;
-        svn_error_t *err;
-
-        nb.inner_func = ctx->notify_func2;
-        nb.inner_baton = ctx->notify_baton2;
-        nb.checkout_abspath = tmp_abspath;
-        nb.final_abspath = dst_abspath;
-        ctx->notify_func2 = notification_adjust_func;
-        ctx->notify_baton2 = &nb;
-
-        err = svn_client__checkout_internal(&pair->src_revnum,
-                                            pair->src_original,
-                                            tmp_abspath,
-                                            &pair->src_peg_revision,
-                                            &pair->src_op_revision,
-                                            svn_depth_infinity,
-                                            ignore_externals, FALSE,
-                                            &sleep_needed, ctx, pool);
+      if (same_repositories)
+        {
+          svn_boolean_t sleep_needed = FALSE;
+          const char *tmpdir_abspath, *tmp_abspath;
 
-        ctx->notify_func2 = old_notify_func2;
-        ctx->notify_baton2 = old_notify_baton2;
+          /* Find a temporary location in which to check out the copy source. 
*/
+          SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, dst_abspath,
+                                     pool, pool));
+                                     
+          SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath,
+                                           svn_io_file_del_on_close, pool, 
pool));
+
+          /* Make a new checkout of the requested source. While doing so,
+           * resolve pair->src_revnum to an actual revision number in case it
+           * was until now 'invalid' meaning 'head'.  Ask this function not to
+           * sleep for timestamps, by passing a sleep_needed output param.
+           * Send notifications for all nodes except the root node, and adjust
+           * them to refer to the destination rather than this temporary path. 
*/
+          {
+            svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2;
+            void *old_notify_baton2 = ctx->notify_baton2;
+            struct notification_adjust_baton nb;
+            svn_error_t *err;
+          
+            nb.inner_func = ctx->notify_func2;
+            nb.inner_baton = ctx->notify_baton2;
+            nb.checkout_abspath = tmp_abspath;
+            nb.final_abspath = dst_abspath;
+            ctx->notify_func2 = notification_adjust_func;
+            ctx->notify_baton2 = &nb;
+          
+            err = svn_client__checkout_internal(&pair->src_revnum,
+                                                pair->src_original,
+                                                tmp_abspath,
+                                                &pair->src_peg_revision,
+                                                &pair->src_op_revision,
+                                                svn_depth_infinity,
+                                                ignore_externals, FALSE,
+                                                &sleep_needed, ctx, pool);
+          
+            ctx->notify_func2 = old_notify_func2;
+            ctx->notify_baton2 = old_notify_baton2;
 
-        SVN_ERR(err);
-      }
+            SVN_ERR(err);
+          }
 
       /* Schedule dst_path for addition in parent, with copy history.
          Don't send any notification here.
          Then remove the temporary checkout's .svn dir in preparation for
          moving the rest of it into the final destination. */
-      if (same_repositories)
-        {
           SVN_ERR(svn_wc_copy3(ctx->wc_ctx, tmp_abspath, dst_abspath,
                                TRUE /* metadata_only */,
                                ctx->cancel_func, ctx->cancel_baton,
@@ -1616,21 +1616,27 @@ repos_to_wc_copy_single(svn_client__copy
         }
       else
         {
-          /* ### We want to schedule this as a simple add, or even better
-             a copy from a foreign repos, but we don't yet have the
-             WC APIs to do that, so we will just move the whole WC into
-             place as a disjoint, nested WC. */
-
-          /* Move the working copy to where it is expected. */
-          SVN_ERR(svn_wc__rename_wc(ctx->wc_ctx, tmp_abspath, dst_abspath,
-                                    pool));
+          if (ctx->notify_func2)
+            {
+              svn_wc_notify_t *notify;
+              notify = svn_wc_create_notify_url(
+                                    pair->src_abspath_or_url,
+                                    svn_wc_notify_foreign_copy_begin,
+                                    pool);
 
-          svn_io_sleep_for_timestamps(dst_abspath, pool);
+              ctx->notify_func2(ctx->notify_baton2, notify, pool);
+            }
 
-          return svn_error_createf(
-             SVN_ERR_UNSUPPORTED_FEATURE, NULL,
-             _("Source URL '%s' is from foreign repository; "
-               "leaving it as a disjoint WC"), pair->src_abspath_or_url);
+          SVN_ERR(svn_client__copy_foreign(pair->src_abspath_or_url,
+                                           dst_abspath,
+                                           &pair->src_peg_revision,
+                                           &pair->src_op_revision,
+                                           svn_depth_infinity,
+                                           FALSE /* make_parents */,
+                                           TRUE /* already_locked */,
+                                           ctx, pool));
+
+          return SVN_NO_ERROR;
         }
     } /* end directory case */
 

Modified: subversion/trunk/subversion/libsvn_client/copy_foreign.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/copy_foreign.c?rev=1437203&r1=1437202&r2=1437203&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/copy_foreign.c (original)
+++ subversion/trunk/subversion/libsvn_client/copy_foreign.c Tue Jan 22 
21:53:37 2013
@@ -85,6 +85,8 @@ edit_open(void *edit_baton,
   db->users = 1;
   db->local_abspath = eb->anchor_abspath;
 
+  SVN_ERR(svn_io_make_dir_recursively(eb->anchor_abspath, dir_pool));
+
   *root_baton = db;
 
   return SVN_NO_ERROR;
@@ -445,6 +447,7 @@ svn_client__copy_foreign(const char *url
                          svn_opt_revision_t *revision,
                          svn_depth_t depth,
                          svn_boolean_t make_parents,
+                         svn_boolean_t already_locked,
                          svn_client_ctx_t *ctx,
                          apr_pool_t *scratch_pool)
 {
@@ -534,24 +537,36 @@ svn_client__copy_foreign(const char *url
               }
           }
 
-      SVN_WC__CALL_WITH_WRITE_LOCK(
-            svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props,
-                                  ctx->notify_func2, ctx->notify_baton2,
-                                  scratch_pool),
-            ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
-
+      if (!already_locked)
+        SVN_WC__CALL_WITH_WRITE_LOCK(
+              svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props,
+                                    ctx->notify_func2, ctx->notify_baton2,
+                                    scratch_pool),
+              ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
+      else
+        SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props,
+                                      ctx->notify_func2, ctx->notify_baton2,
+                                      scratch_pool));
     }
   else
     {
-      SVN_WC__CALL_WITH_WRITE_LOCK(
-            copy_foreign_dir(ra_session, loc,
-                             ctx->wc_ctx, dst_abspath,
-                             depth,
-                             ctx->notify_func2, ctx->notify_baton2,
-                             ctx->cancel_func, ctx->cancel_baton,
-                             scratch_pool),
-            ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
+      if (!already_locked)
+        SVN_WC__CALL_WITH_WRITE_LOCK(
+              copy_foreign_dir(ra_session, loc,
+                               ctx->wc_ctx, dst_abspath,
+                               depth,
+                               ctx->notify_func2, ctx->notify_baton2,
+                               ctx->cancel_func, ctx->cancel_baton,
+                               scratch_pool),
+              ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
+      else
+        SVN_ERR(copy_foreign_dir(ra_session, loc,
+                                 ctx->wc_ctx, dst_abspath,
+                                 depth,
+                                 ctx->notify_func2, ctx->notify_baton2,
+                                 ctx->cancel_func, ctx->cancel_baton,
+                                 scratch_pool));
     }
 
   return SVN_NO_ERROR;
-}
\ No newline at end of file
+}

Modified: subversion/trunk/subversion/svn/notify.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/notify.c?rev=1437203&r1=1437202&r2=1437203&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/notify.c (original)
+++ subversion/trunk/subversion/svn/notify.c Tue Jan 22 21:53:37 2013
@@ -1003,6 +1003,14 @@ notify(void *baton, const svn_wc_notify_
     case svn_wc_notify_conflict_resolver_done:
       break;
 
+    case svn_wc_notify_foreign_copy_begin:
+      if (n->merge_range == NULL)
+        err = svn_cmdline_printf(pool,
+                                 _("--- Copying from foreign repository URL 
'%s':\n"),
+                                 n->url);
+      if (err)
+        goto print_error;
+
     default:
       break;
     }

Modified: subversion/trunk/subversion/tests/cmdline/copy_tests.py
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/copy_tests.py?rev=1437203&r1=1437202&r2=1437203&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/copy_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/copy_tests.py Tue Jan 22 21:53:37 
2013
@@ -907,7 +907,7 @@ def wc_to_repos(sbox):
 #----------------------------------------------------------------------
 # Issue 1090: various use-cases of 'svn cp URL wc' where the
 # repositories might be different, or be the same repository.
-@Issues(1090,1444)
+@Issues(1090, 1444, 3590)
 def repos_to_wc(sbox):
   "repository to working-copy copy"
 
@@ -988,9 +988,14 @@ def repos_to_wc(sbox):
   E_url = other_repo_url + "/A/B/E"
   pi_url = other_repo_url + "/A/D/G/pi"
 
-  # Expect an error in the directory case until we allow this copy to succeed.
-  expected_error = "svn: E200007: Source URL '.*foreign repository"
-  svntest.actions.run_and_verify_svn(None, None, expected_error,
+  # Finally, for 1.8 we allow this copy to succeed.
+  expected_output = svntest.verify.UnorderedOutput([
+    '--- Copying from foreign repository URL \'%s\':\n' % E_url,
+    'A         %s\n' % sbox.ospath('E'),
+    'A         %s\n' % sbox.ospath('E/beta'),
+    'A         %s\n' % sbox.ospath('E/alpha'),
+  ])
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'copy', E_url, wc_dir)
 
   # But file case should work fine.
@@ -999,6 +1004,10 @@ def repos_to_wc(sbox):
   expected_output = svntest.actions.get_virginal_state(wc_dir, 1)
   expected_output.add({
     'pi' : Item(status='A ',  wc_rev='0', entry_rev='1'),
+    # And from the foreign repository
+    'E'             : Item(status='A ', wc_rev='0'),
+    'E/beta'        : Item(status='A ', wc_rev='0'),
+    'E/alpha'       : Item(status='A ', wc_rev='0'),
     })
   svntest.actions.run_and_verify_status(wc_dir, expected_output)
 

Modified: subversion/trunk/subversion/tests/libsvn_client/client-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_client/client-test.c?rev=1437203&r1=1437202&r2=1437203&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_client/client-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_client/client-test.c Tue Jan 22 
21:53:37 2013
@@ -755,7 +755,7 @@ test_foreign_repos_copy(const svn_test_o
   SVN_ERR(svn_client__copy_foreign(svn_path_url_add_component2(repos2_url, "A",
                                                                pool),
                                    svn_dirent_join(wc_path, "A-copied", pool),
-                                   &peg_rev, &rev, svn_depth_infinity, FALSE,
+                                   &peg_rev, &rev, svn_depth_infinity, FALSE, 
FALSE,
                                    ctx, pool));
 
 
@@ -763,7 +763,7 @@ test_foreign_repos_copy(const svn_test_o
                                                                "iota",
                                                                pool),
                                    svn_dirent_join(wc_path, "iota-copied", 
pool),
-                                   &peg_rev, &rev, svn_depth_infinity, FALSE,
+                                   &peg_rev, &rev, svn_depth_infinity, FALSE, 
FALSE,
                                    ctx, pool));
 
   return SVN_NO_ERROR;


Reply via email to