Return-Path: X-Original-To: apmail-subversion-commits-archive@minotaur.apache.org Delivered-To: apmail-subversion-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 9BCB1171CD for ; Thu, 1 Oct 2015 11:41:49 +0000 (UTC) Received: (qmail 37729 invoked by uid 500); 1 Oct 2015 11:41:49 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 37695 invoked by uid 500); 1 Oct 2015 11:41:49 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 37441 invoked by uid 99); 1 Oct 2015 11:41:49 -0000 Received: from Unknown (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 01 Oct 2015 11:41:49 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id B436D1A3657 for ; Thu, 1 Oct 2015 11:41:48 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.802 X-Spam-Level: * X-Spam-Status: No, score=1.802 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=0.001, URIBL_BLOCKED=0.001] autolearn=disabled Received: from mx1-eu-west.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id Aw47Xfu9PNbC for ; Thu, 1 Oct 2015 11:41:42 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-eu-west.apache.org (ASF Mail Server at mx1-eu-west.apache.org) with ESMTP id 1F7BF20592 for ; Thu, 1 Oct 2015 11:41:41 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 45D1EE0231 for ; Thu, 1 Oct 2015 11:41:40 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id EB5323A01DC for ; Thu, 1 Oct 2015 11:41:39 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1706217 - in /subversion/trunk/subversion: libsvn_client/patch.c tests/cmdline/patch_tests.py Date: Thu, 01 Oct 2015 11:41:39 -0000 To: commits@subversion.apache.org From: rhuijben@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20151001114139.EB5323A01DC@svn01-us-west.apache.org> 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__':