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),