You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/10/01 13:41:39 UTC
svn commit: r1706217 - in /subversion/trunk/subversion:
libsvn_client/patch.c tests/cmdline/patch_tests.py
Author: rhuijben
Date: Thu Oct 1 11:41:39 2015
New Revision: 1706217
URL: http://svn.apache.org/viewvc?rev=1706217&view=rev
Log:
Record the patch dry run information in a way that even patches that replace
files can give acurate results.
* subversion/libsvn_client/patch.c
(patch_target_info_t): Add boolean for adds.
(target_is_added,
target_is_deleted): New functions.
(resolve_target_path): Handle self-deleted as not existing.
(init_patch_target,
apply_one_patch): Pass info.
(create_missing_parents): Use target info instead of hashtable.
Move cancel function up a bit.
(install_patched_target): Pass info instead of hash.
(apply_patches): Record info a bit later to allow processing just
earlier items.
* subversion/tests/cmdline/patch_tests.py
(patch_deletes_prop): Enable dry-run.
(patch_git_symlink): New function.
(test_list): Add patch_git_symlink.
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=1706217&r1=1706216&r2=1706217&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Thu Oct 1 11:41:39 2015
@@ -273,8 +273,58 @@ typedef struct patch_target_t {
typedef struct patch_target_info_t {
const char *local_abspath;
svn_boolean_t deleted;
+ svn_boolean_t added;
} patch_target_info_t;
+/* Check if LOCAL_ABSPATH is recorded as added in TARGETS_INFO */
+static svn_boolean_t
+target_is_added(const apr_array_header_t *targets_info,
+ const char *local_abspath,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+
+ for (i = targets_info->nelts - 1; i >= 0; i--)
+ {
+ const patch_target_info_t *target_info =
+ APR_ARRAY_IDX(targets_info, i, const patch_target_info_t *);
+
+ const char *info = svn_dirent_skip_ancestor(target_info->local_abspath,
+ local_abspath);
+
+ if (info && !*info)
+ return target_info->added;
+ else if (info)
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+/* Check if LOCAL_ABSPATH or an ancestor is recorded as deleted in
+ TARGETS_INFO */
+static svn_boolean_t
+target_is_deleted(const apr_array_header_t *targets_info,
+ const char *local_abspath,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+
+ for (i = targets_info->nelts - 1; i >= 0; i--)
+ {
+ const patch_target_info_t *target_info =
+ APR_ARRAY_IDX(targets_info, i, const patch_target_info_t *);
+
+ const char *info = svn_dirent_skip_ancestor(target_info->local_abspath,
+ local_abspath);
+
+ if (info)
+ return target_info->deleted;
+ }
+
+ return FALSE;
+}
+
/* Strip STRIP_COUNT components from the front of PATH, returning
* the result in *RESULT, allocated in RESULT_POOL.
@@ -392,6 +442,7 @@ resolve_target_path(patch_target_t *targ
int strip_count,
svn_boolean_t has_text_changes,
svn_wc_context_t *wc_ctx,
+ const apr_array_header_t *targets_info,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -454,6 +505,13 @@ resolve_target_path(patch_target_t *targ
return SVN_NO_ERROR;
}
+ if (target_is_deleted(targets_info, target->local_abspath, scratch_pool))
+ {
+ target->locally_deleted = TRUE;
+ target->db_kind = svn_node_none;
+ return SVN_NO_ERROR;
+ }
+
/* Skip things we should not be messing with. */
err = svn_wc_status3(&status, wc_ctx, target->local_abspath,
result_pool, scratch_pool);
@@ -979,6 +1037,7 @@ init_patch_target(patch_target_t **patch
const char *root_abspath,
svn_wc_context_t *wc_ctx, int strip_count,
svn_boolean_t remove_tempfiles,
+ const apr_array_header_t *targets_info,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
patch_target_t *target;
@@ -1008,7 +1067,7 @@ init_patch_target(patch_target_t **patch
SVN_ERR(resolve_target_path(target, choose_target_filename(patch),
root_abspath, strip_count, has_text_changes,
- wc_ctx, result_pool, scratch_pool));
+ wc_ctx, targets_info, result_pool, scratch_pool));
*patch_target = target;
if (! target->skipped)
{
@@ -2334,6 +2393,7 @@ apply_one_patch(patch_target_t **patch_t
int strip_count,
svn_boolean_t ignore_whitespace,
svn_boolean_t remove_tempfiles,
+ const apr_array_header_t *targets_info,
svn_client_patch_func_t patch_func,
void *patch_baton,
svn_cancel_func_t cancel_func,
@@ -2349,7 +2409,8 @@ apply_one_patch(patch_target_t **patch_t
svn_boolean_t has_text_changes = FALSE;
SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count,
- remove_tempfiles, result_pool, scratch_pool));
+ remove_tempfiles, targets_info,
+ result_pool, scratch_pool));
if (target->skipped)
{
*patch_target = target;
@@ -2821,7 +2882,7 @@ create_missing_parents(patch_target_t *t
const char *abs_wc_path,
svn_client_ctx_t *ctx,
svn_boolean_t dry_run,
- apr_hash_t *already_added,
+ apr_array_header_t *targets_info,
apr_pool_t *scratch_pool)
{
const char *local_abspath;
@@ -2902,33 +2963,41 @@ create_missing_parents(patch_target_t *t
for (i = present_components; i < components->nelts - 1; i++)
{
const char *component;
+ patch_target_info_t *pti;
svn_pool_clear(iterpool);
+ if (ctx->cancel_func)
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+
component = APR_ARRAY_IDX(components, i, const char *);
local_abspath = svn_dirent_join(local_abspath, component,
- scratch_pool);
+ iterpool);
+
+ if (target_is_added(targets_info, local_abspath, iterpool))
+ continue;
+
+ pti = apr_pcalloc(targets_info->pool, sizeof(*pti));
+
+ pti->local_abspath = apr_pstrdup(targets_info->pool,
+ local_abspath);
+ pti->added = TRUE;
+
+ APR_ARRAY_PUSH(targets_info, patch_target_info_t *) = pti;
+
if (dry_run)
{
- if (!svn_hash_gets(already_added, local_abspath))
+ if (ctx->notify_func2)
{
- svn_hash_sets(already_added,
- apr_pstrdup(apr_hash_pool_get(already_added),
- local_abspath),
- "");
-
- if (ctx->notify_func2)
- {
- /* Just do notification. */
- svn_wc_notify_t *notify;
- notify = svn_wc_create_notify(local_abspath,
- svn_wc_notify_add,
- iterpool);
- notify->kind = svn_node_dir;
- ctx->notify_func2(ctx->notify_baton2, notify,
- iterpool);
- }
- }
+ /* Just do notification. */
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify(local_abspath,
+ svn_wc_notify_add,
+ iterpool);
+ notify->kind = svn_node_dir;
+ ctx->notify_func2(ctx->notify_baton2, notify,
+ iterpool);
+ }
}
else
{
@@ -2936,10 +3005,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. */
-
- if (ctx->cancel_func)
- SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
-
SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath,
NULL /*props*/,
FALSE /* skip checks */,
@@ -2965,7 +3030,8 @@ create_missing_parents(patch_target_t *t
static svn_error_t *
install_patched_target(patch_target_t *target, const char *abs_wc_path,
svn_client_ctx_t *ctx, svn_boolean_t dry_run,
- apr_hash_t *already_added, apr_pool_t *pool)
+ apr_array_header_t *targets_info,
+ apr_pool_t *pool)
{
if (target->deleted)
{
@@ -3016,7 +3082,7 @@ install_patched_target(patch_target_t *t
}
else
SVN_ERR(create_missing_parents(target, abs_wc_path, ctx,
- dry_run, already_added, pool));
+ dry_run, targets_info, pool));
}
else
@@ -3415,7 +3481,6 @@ apply_patches(/* The path to the patch f
apr_pool_t *iterpool;
svn_patch_file_t *patch_file;
apr_array_header_t *targets_info;
- apr_hash_t *already_added = apr_hash_make(scratch_pool);
/* Try to open the patch file. */
SVN_ERR(svn_diff_open_patch_file(&patch_file, patch_abspath, scratch_pool));
@@ -3441,6 +3506,7 @@ apply_patches(/* The path to the patch f
SVN_ERR(apply_one_patch(&target, patch, root_abspath,
ctx->wc_ctx, strip_count,
ignore_whitespace, remove_tempfiles,
+ targets_info,
patch_func, patch_baton,
ctx->cancel_func, ctx->cancel_baton,
iterpool, iterpool));
@@ -3452,19 +3518,17 @@ apply_patches(/* The path to the patch f
target_info->local_abspath = apr_pstrdup(scratch_pool,
target->local_abspath);
target_info->deleted = target->deleted;
+ target_info->added = target->added;
if (! target->skipped)
{
- APR_ARRAY_PUSH(targets_info,
- patch_target_info_t *) = target_info;
-
if (target->has_text_changes
|| target->added
|| target->move_target_abspath
|| target->deleted)
SVN_ERR(install_patched_target(target, root_abspath,
ctx, dry_run,
- already_added, iterpool));
+ targets_info, iterpool));
if (target->has_prop_changes && (!target->deleted))
SVN_ERR(install_patched_prop_targets(target, ctx,
@@ -3472,12 +3536,9 @@ apply_patches(/* The path to the patch f
SVN_ERR(write_out_rejected_hunks(target, dry_run, iterpool));
- if (target->added)
- svn_hash_sets(already_added,
- apr_pstrdup(scratch_pool,
- target->local_abspath),
- "");
- }
+ APR_ARRAY_PUSH(targets_info,
+ patch_target_info_t *) = target_info;
+ }
SVN_ERR(send_patch_notification(target, ctx, iterpool));
if (target->deleted && !target->skipped)
Modified: subversion/trunk/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/patch_tests.py?rev=1706217&r1=1706216&r2=1706217&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/patch_tests.py Thu Oct 1 11:41:39 2015
@@ -3711,7 +3711,7 @@ def patch_deletes_prop(sbox):
expected_skip,
None, # expected err
1, # check-props
- 0) # dry-run
+ 1) # dry-run
# Revert any local mods, then try to reverse-apply a patch which
# *adds* the property.
@@ -6730,6 +6730,119 @@ def patch_add_remove_executable(sbox):
[], True, True,
'--reverse-diff')
+def patch_git_symlink(sbox):
+ "patch a git symlink"
+
+ # ### Currently we completely ignore the symlink behavior via mode in
+ # ### Subversion but writing this test already found a few bugs in the
+ # ### patch code.
+
+ sbox.build(read_only = True)
+ wc_dir = sbox.wc_dir
+
+ patch_add = [
+ 'diff --git a/link-to-iota b/link-to-iota\n',
+ 'new file mode 120000\n',
+ 'index 0000000..3ef26e4\n',
+ '--- /dev/null\n',
+ '+++ b/link-to-iota\n',
+ '@@ -0,0 +1 @@\n',
+ '+iota\n',
+ '\ No newline at end of file\n',
+ ]
+
+ patch_edit = [
+ 'diff --git a/link-to-iota b/link-to-iota\n',
+ 'index 3ef26e4..33e5b38 120000\n',
+ '--- a/link-to-iota\n',
+ '+++ b/link-to-iota\n',
+ '@@ -1 +1 @@\n',
+ '-iota\n',
+ '\ No newline at end of file\n',
+ '+A/mu\n',
+ '\ No newline at end of file\n',
+ ]
+
+ patch_to_file = [
+ 'diff --git a/link-to-iota b/link-to-iota\n',
+ 'deleted file mode 120000\n',
+ 'index 33e5b38..0000000\n',
+ '--- a/link-to-iota\n',
+ '+++ /dev/null\n',
+ '@@ -1 +0,0 @@\n',
+ '-A/mu\n',
+ '\ No newline at end of file\n',
+ 'diff --git a/link-to-iota b/link-to-iota\n',
+ 'new file mode 100644\n',
+ 'index 0000000..1b130bf\n',
+ '--- /dev/null\n',
+ '+++ b/link-to-iota\n',
+ '@@ -0,0 +1 @@\n',
+ '+This is a real file\n',
+ ]
+
+ add_patch = sbox.get_tempname('add.patch')
+ svntest.main.file_write(add_patch, ''.join(patch_add), mode='wb')
+
+ edit_patch = sbox.get_tempname('edit.patch')
+ svntest.main.file_write(edit_patch, ''.join(patch_edit), mode='wb')
+
+ to_file_patch = sbox.get_tempname('to_file.patch')
+ svntest.main.file_write(to_file_patch, ''.join(patch_to_file), mode='wb')
+
+
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+ expected_status.add({
+ 'link-to-iota' : Item(status='A ', wc_rev='-'),
+ })
+ expected_output = svntest.wc.State(wc_dir, {
+ 'link-to-iota' : Item(status='A '),
+ })
+ expected_disk = svntest.main.greek_state.copy()
+ expected_disk.add({
+ 'link-to-iota' : Item(contents="iota"),
+ })
+ expected_skip = svntest.wc.State(wc_dir, {})
+
+ svntest.actions.run_and_verify_patch(wc_dir, add_patch,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], True, True)
+
+ # And again
+ expected_output.tweak('link-to-iota', status='G ')
+ svntest.actions.run_and_verify_patch(wc_dir, add_patch,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], True, True)
+
+ # Now tweak the link
+ expected_disk.tweak('link-to-iota', contents='A/mu')
+ svntest.actions.run_and_verify_patch(wc_dir, edit_patch,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], True, True)
+
+ # And again
+ svntest.actions.run_and_verify_patch(wc_dir, edit_patch,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], True, True)
+
+ # And replace the link with a file
+ expected_output.tweak('link-to-iota', status='A ', prev_status='D ')
+ expected_disk.tweak('link-to-iota', contents="This is a real file\n")
+ svntest.actions.run_and_verify_patch(wc_dir, to_file_patch,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], True, True)
+
+ # And again
+ # svntest.actions.run_and_verify_patch(wc_dir, to_file_patch,
+ # expected_output, expected_disk,
+ # expected_status, expected_skip,
+ # [], True, True)
+
########################################################################
#Run the tests
@@ -6804,6 +6917,7 @@ test_list = [ None,
patch_prop_madness,
patch_empty_vs_delete,
patch_add_remove_executable,
+ patch_git_symlink,
]
if __name__ == '__main__':