Author: rhuijben
Date: Thu Jul 29 00:49:57 2010
New Revision: 980278

URL: http://svn.apache.org/viewvc?rev=980278&view=rev
Log:
Make 'svn patch' handle adding directories over deleted nodes. This
feature was documented as 'needs wc-ng', while replacing directories just
works in 1.6. The old implementation just needed fixing for single db.

And even with multi-db we allow replacing a deleted file with a
directory; so why wait?

* subversion/libsvn_client/patch.c
  (create_missing_parents):
     Don't copy a constant input argument to a tempvar. Allow replacing
     any deleted node as long as there is no in-wc obstruction.
     Just create all directory levels at once, to support single-db (where
     deleted directories do not exist) and not-single db (where they do)
     wc-ng with the same code.

* subversion/tests/cmdline/patch_tests.py
  (patch_add_new_dir): Update expected values to expect an addition
    of A/C/Y/new.

Modified:
    subversion/trunk/subversion/libsvn_client/patch.c
    subversion/trunk/subversion/tests/cmdline/patch_tests.py

Modified: subversion/trunk/subversion/libsvn_client/patch.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/patch.c?rev=980278&r1=980277&r2=980278&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Thu Jul 29 00:49:57 2010
@@ -1823,14 +1823,15 @@ create_missing_parents(patch_target_t *t
   apr_pool_t *iterpool;
 
   /* Check if we can safely create the target's parent. */
-  local_abspath = apr_pstrdup(scratch_pool, abs_wc_path);
+  local_abspath = abs_wc_path;
   components = svn_path_decompose(target->local_relpath, scratch_pool);
   present_components = 0;
   iterpool = svn_pool_create(scratch_pool);
   for (i = 0; i < components->nelts - 1; i++)
     {
       const char *component;
-      svn_node_kind_t kind;
+      svn_node_kind_t wc_kind, disk_kind;
+      svn_boolean_t is_deleted;
 
       svn_pool_clear(iterpool);
 
@@ -1838,49 +1839,45 @@ create_missing_parents(patch_target_t *t
                                 const char *);
       local_abspath = svn_dirent_join(local_abspath, component, scratch_pool);
 
-      SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, local_abspath, TRUE,
+      SVN_ERR(svn_wc_read_kind(&wc_kind, ctx->wc_ctx, local_abspath, TRUE,
                                iterpool));
-      if (kind == svn_node_file)
+
+      SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, iterpool));
+
+      if (wc_kind != svn_node_none)
+        SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted,
+                                               ctx->wc_ctx,
+                                               local_abspath,
+                                               iterpool));
+      else
+        is_deleted = FALSE;
+
+      if (disk_kind == svn_node_file
+          || (wc_kind == svn_node_file && !is_deleted))
         {
-          /* Obstructed. */
+          /* on-disk files and missing files are obstructions */
           target->skipped = TRUE;
           break;
         }
-      else if (kind == svn_node_dir)
+      else if (wc_kind == svn_node_dir)
         {
-          /* ### wc-ng should eventually be able to replace
-           * directories in-place, so this schedule conflict
-           * check will go away. We could then also make the
-           * svn_wc_read_kind() call above ignore hidden
-           * nodes.*/
-          svn_boolean_t is_deleted;
-
-          SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted,
-                                                 ctx->wc_ctx,
-                                                 local_abspath,
-                                                 iterpool));
           if (is_deleted)
-            {
-              target->skipped = TRUE;
-              break;
-            }
+            break;
 
+          /* continue one level deeper */
           present_components++;
         }
+      else if (disk_kind == svn_node_dir)
+        {
+          /* Obstructed. ### BH: why? We can just add a directory */
+          target->skipped = TRUE;
+          break;
+        }
       else
         {
-          /* The WC_DB doesn't know much about this node.
-           * Check what's on disk. */
-          svn_node_kind_t disk_kind;
-
-          SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
-                                    iterpool));
-          if (disk_kind != svn_node_none)
-            {
-              /* An unversioned item is in the way. */
-              target->skipped = TRUE;
-              break;
-            }
+          /* It's not a file, it's not a dir.. 
+             Let's add a dir */
+          break;
         }
     }
 
@@ -1896,6 +1893,15 @@ create_missing_parents(patch_target_t *t
                                           component, scratch_pool);
         }
 
+      if (!dry_run && present_components < components->nelts - 1)
+        SVN_ERR(svn_io_make_dir_recursively(
+                        svn_dirent_join(
+                                   abs_wc_path,
+                                   svn_relpath_dirname(target->local_relpath,
+                                                       scratch_pool),
+                                   scratch_pool),
+                        scratch_pool));
+
       for (i = present_components; i < components->nelts - 1; i++)
         {
           const char *component;
@@ -1926,8 +1932,6 @@ create_missing_parents(patch_target_t *t
                * to version control. Allow cancellation since we
                * have not modified the working copy yet for this
                * target. */
-              SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
-                                      iterpool));
               SVN_ERR(svn_wc_add4(ctx->wc_ctx, local_abspath,
                                   svn_depth_infinity,
                                   NULL, SVN_INVALID_REVNUM,

Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=980278&r1=980277&r2=980278&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Thu Jul 29 
00:49:57 2010
@@ -970,27 +970,35 @@ def patch_add_new_dir(sbox):
     'A         %s\n' % os.path.join(wc_dir, 'X'),
     'A         %s\n' % os.path.join(wc_dir, 'X', 'Y'),
     'A         %s\n' % os.path.join(wc_dir, 'X', 'Y', 'new'),
-    'Skipped missing target: \'%s\'\n' % A_C_Y_new_path,
+    'A         %s\n' % os.path.join(wc_dir, 'A', 'C'),
+    'A         %s\n' % os.path.join(wc_dir, 'A', 'C', 'Y'),
+    'A         %s\n' % os.path.join(wc_dir, 'A', 'C', 'Y', 'new'),
     'Skipped missing target: \'%s\'\n' % A_Z_new_path,
     'Summary of conflicts:\n',
-    '  Skipped paths: 2\n',
+    '  Skipped paths: 1\n',
   ]
 
   # Create the unversioned obstructing directory
   os.mkdir(os.path.dirname(A_Z_new_path))
 
   expected_disk = svntest.main.greek_state.copy()
-  expected_disk.add({'X/Y/new': Item(contents='new\n')})
-  expected_disk.add({'A/Z': Item()})
-
-  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
-  expected_status.add({'X' : Item(status='A ', wc_rev=0)})
-  expected_status.add({'X/Y' : Item(status='A ', wc_rev=0)})
-  expected_status.add({'X/Y/new' : Item(status='A ', wc_rev=0)})
-  expected_status.add({'A/C' : Item(status='D ', wc_rev=1)})
+  expected_disk.add({
+           'X/Y/new'   : Item(contents='new\n'),
+           'A/C/Y/new' : Item(contents='new\n'),
+           'A/Z'       : Item(),
+  })
+
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({
+           'X'         : Item(status='A ', wc_rev=0),
+           'X/Y'       : Item(status='A ', wc_rev=0),
+           'X/Y/new'   : Item(status='A ', wc_rev=0),
+           'A/C'       : Item(status='R ', wc_rev=1),
+           'A/C/Y'     : Item(status='A ', wc_rev=0),
+           'A/C/Y/new' : Item(status='A ', wc_rev=0),
+  })
 
-  expected_skip = wc.State('', {A_C_Y_new_path : Item(),
-                                A_Z_new_path : Item() })
+  expected_skip = wc.State('', {A_Z_new_path : Item() })
 
   svntest.actions.run_and_verify_patch(wc_dir,
                                        os.path.abspath(patch_file_path),


Reply via email to