I'm a little bit confused... After this commit, it seems that some tests
are failing:
[[[
W: Unexpected output
W: EXPECTED STDERR (regexp, match_all=False):
W: | svn:.*is not a local path
W: ACTUAL STDERR:
W: | svn: E200009:
'D:\svn\svn-trunk5\out\build\x64-Debug\Testing\subversion\tests\cmdline\svn-test-work\working_copies\input_validation_tests-20\foo'
does not exist
W: CWD:
D:\svn\svn-trunk5\out\build\x64-Debug\Testing\subversion\tests\cmdline
W: EXCEPTION: SVNUnmatchedError
Traceback (most recent call last):
File "D:\svn\svn-trunk5\subversion\tests\cmdline\svntest\main.py", line
1986, in run
rc = self.pred.run(sandbox)
^^^^^^^^^^^^^^^^^^^^^^
File "D:\svn\svn-trunk5\subversion\tests\cmdline\svntest\testcase.py",
line 178, in run
result = self.func(sandbox)
^^^^^^^^^^^^^^^^^^
File
"D:\svn\svn-trunk5\subversion\tests\cmdline\input_validation_tests.py",
line 226, in invalid_patch_targets
run_and_verify_svn_in_wc(sbox, "svn:.*is not a local path", 'patch',
File
"D:\svn\svn-trunk5\subversion\tests\cmdline\input_validation_tests.py",
line 55, in run_and_verify_svn_in_wc
svntest.actions.run_and_verify_svn([], expected_stderr,
File "D:\svn\svn-trunk5\subversion\tests\cmdline\svntest\actions.py",
line 339, in run_and_verify_svn
return run_and_verify_svn2(expected_stdout, expected_stderr,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\svn\svn-trunk5\subversion\tests\cmdline\svntest\actions.py",
line 379, in run_and_verify_svn2
verify.verify_outputs("Unexpected output", out, err,
File "D:\svn\svn-trunk5\subversion\tests\cmdline\svntest\verify.py", line
532, in verify_outputs
compare_and_display_lines(message, label, expected, actual, raisable)
File "D:\svn\svn-trunk5\subversion\tests\cmdline\svntest\verify.py", line
505, in compare_and_display_lines
raise raisable
svntest.main.SVNUnmatchedError
FAIL: input_validation_tests.py 20: non-working copy paths for 'patch'
]]]
The reason for this is I've accidently changed the sequence in which
parameters are verifying. Now the `svn patch` command verifies that the
patch file is openable before validating a working copy. However, I think
that the new behaviour is more correct because it seems like the patch file
is something we should look into first. Like this is a more important part
of the command. Even in the cmdline it goes first.
So is it okay to do this little change in behaviour and adjust the tests a
little bit? Or this doesn't follow backward compatibility guidelines?
On Thu, May 8, 2025 at 4:19 PM <[email protected]> wrote:
> Author: rinrab
> Date: Thu May 8 14:19:32 2025
> New Revision: 1925463
>
> URL: http://svn.apache.org/viewvc?rev=1925463&view=rev
> Log:
> Add svn_client_patch2 function that accesses the patch file from an
> APR file instead of using a local path.
>
> * subversion/include/svn_client.h
> (svn_client_patch2): New function.
> (svn_client_patch): Deprecate.
> * subversion/libsvn_client/deprecated.c
> (svn_client_patch): Add and implement through svn_client_patch2().
> * subversion/libsvn_client/patch.c
> (apply_patches): Accept a file instead of a path and create a
> svn_diff_patch_parser_t instead of opening a svn_patch_file_t.
> (svn_client_patch): Update implementation to svn_client_patch2.
> * subversion/svn/patch-cmd.c
> (svn_cl__patch): Check and open the patch file manually, pass to
> the new function, and close it then.
> * subversion/tests/libsvn_client/client-test.c
> (test_patch): Update patch api used.
>
> See also a related discussion with myself on dev@ where this was
> initiated:
> https://lists.apache.org/thread/p260hdrt5wx0tv6xs9l5nt3pvbzvnrv4
>
> Modified:
> subversion/trunk/subversion/include/svn_client.h
> subversion/trunk/subversion/libsvn_client/deprecated.c
> subversion/trunk/subversion/libsvn_client/patch.c
> subversion/trunk/subversion/svn/patch-cmd.c
> subversion/trunk/subversion/tests/libsvn_client/client-test.c
>
> Modified: subversion/trunk/subversion/include/svn_client.h
> URL:
> http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1925463&r1=1925462&r2=1925463&view=diff
>
> ==============================================================================
> --- subversion/trunk/subversion/include/svn_client.h (original)
> +++ subversion/trunk/subversion/include/svn_client.h Thu May 8 14:19:32
> 2025
> @@ -7718,8 +7718,9 @@ typedef svn_error_t *(*svn_client_patch_
> apr_pool_t *scratch_pool);
>
> /**
> - * Apply a unidiff patch that's located at absolute path
> - * @a patch_abspath to the working copy directory at @a wc_dir_abspath.
> + * Apply a unidiff patch, described in @a apr_file which should have
> + * @c APR_READ and @c APR_BUFFERED capabilities to the working copy
> + * directory at @a wc_dir_abspath.
> *
> * This function makes a best-effort attempt at applying the patch.
> * It might skip patch targets which cannot be patched (e.g. targets
> @@ -7756,7 +7757,26 @@ typedef svn_error_t *(*svn_client_patch_
> *
> * Use @a scratch_pool for temporary allocations.
> *
> - * @since New in 1.7.
> + * @since New in 1.15.
> + */
> +svn_error_t *
> +svn_client_patch2(apr_file_t *apr_file,
> + const char *wc_dir_abspath,
> + svn_boolean_t dry_run,
> + int strip_count,
> + svn_boolean_t reverse,
> + svn_boolean_t ignore_whitespace,
> + svn_boolean_t remove_tempfiles,
> + svn_client_patch_func_t patch_func,
> + void *patch_baton,
> + svn_client_ctx_t *ctx,
> + apr_pool_t *scratch_pool);
> +
> +/**
> + * Similar to svn_client_patch2(), but accessing the patch by an absolute
> + * path.
> + *
> + * @deprecated Provided for backward compatibility with the 1.7 API.
> */
> svn_error_t *
> svn_client_patch(const char *patch_abspath,
>
> Modified: subversion/trunk/subversion/libsvn_client/deprecated.c
> URL:
> http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/deprecated.c?rev=1925463&r1=1925462&r2=1925463&view=diff
>
> ==============================================================================
> --- subversion/trunk/subversion/libsvn_client/deprecated.c (original)
> +++ subversion/trunk/subversion/libsvn_client/deprecated.c Thu May 8
> 14:19:32 2025
> @@ -3290,3 +3290,46 @@ svn_client_upgrade(const char *path,
> return svn_error_trace(svn_client_upgrade2(NULL, path, NULL, ctx,
> NULL, scratch_pool));
> }
> +
> +svn_error_t *
> +svn_client_patch(const char *patch_abspath,
> + const char *wc_dir_abspath,
> + svn_boolean_t dry_run,
> + int strip_count,
> + svn_boolean_t reverse,
> + svn_boolean_t ignore_whitespace,
> + svn_boolean_t remove_tempfiles,
> + svn_client_patch_func_t patch_func,
> + void *patch_baton,
> + svn_client_ctx_t *ctx,
> + apr_pool_t *scratch_pool)
> +{
> + svn_node_kind_t kind;
> + apr_file_t *apr_file;
> +
> + SVN_ERR(svn_io_check_path(patch_abspath, &kind, scratch_pool));
> + if (kind == svn_node_none)
> + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
> + _("'%s' does not exist"),
> + svn_dirent_local_style(patch_abspath,
> + scratch_pool));
> + if (kind != svn_node_file)
> + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
> + _("'%s' is not a file"),
> + svn_dirent_local_style(patch_abspath,
> + scratch_pool));
> +
> + SVN_ERR(svn_io_file_open(&apr_file, patch_abspath,
> + APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
> + scratch_pool));
> +
> + SVN_ERR(svn_client_patch2(apr_file, wc_dir_abspath,
> + dry_run, strip_count, reverse,
> + ignore_whitespace, remove_tempfiles,
> + patch_func, patch_baton,
> + ctx, scratch_pool));
> +
> + SVN_ERR(svn_io_file_close(apr_file, scratch_pool));
> +
> + return SVN_NO_ERROR;
> +}
>
> Modified: subversion/trunk/subversion/libsvn_client/patch.c
> URL:
> http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/patch.c?rev=1925463&r1=1925462&r2=1925463&view=diff
>
> ==============================================================================
> --- subversion/trunk/subversion/libsvn_client/patch.c (original)
> +++ subversion/trunk/subversion/libsvn_client/patch.c Thu May 8 14:19:32
> 2025
> @@ -3613,8 +3613,8 @@ check_ancestor_delete(const char *delete
>
> /* This function is the main entry point into the patch code. */
> static svn_error_t *
> -apply_patches(/* The path to the patch file. */
> - const char *patch_abspath,
> +apply_patches(/* The patch file. */
> + apr_file_t *apr_file,
> /* The abspath to the working copy the patch should be
> applied to. */
> const char *root_abspath,
> /* Indicates whether we're doing a dry run. */
> @@ -3636,11 +3636,10 @@ apply_patches(/* The path to the patch f
> {
> svn_patch_t *patch;
> apr_pool_t *iterpool;
> - svn_patch_file_t *patch_file;
> + svn_diff_patch_parser_t *patch_parser;
> apr_array_header_t *targets_info;
>
> - /* Try to open the patch file. */
> - SVN_ERR(svn_diff_open_patch_file(&patch_file, patch_abspath,
> scratch_pool));
> + patch_parser = svn_diff_patch_parser_create(apr_file, scratch_pool);
>
> /* Apply patches. */
> targets_info = apr_array_make(scratch_pool, 0,
> @@ -3653,9 +3652,9 @@ apply_patches(/* The path to the patch f
> if (ctx->cancel_func)
> SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
>
> - SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
> - reverse, ignore_whitespace,
> - iterpool, iterpool));
> + SVN_ERR(svn_diff_patch_parser_next(&patch, patch_parser,
> + reverse, ignore_whitespace,
> + iterpool, iterpool));
> if (patch)
> {
> patch_target_t *target;
> @@ -3720,24 +3719,23 @@ apply_patches(/* The path to the patch f
> }
> while (patch);
>
> - SVN_ERR(svn_diff_close_patch_file(patch_file, iterpool));
> svn_pool_destroy(iterpool);
>
> return SVN_NO_ERROR;
> }
>
> svn_error_t *
> -svn_client_patch(const char *patch_abspath,
> - const char *wc_dir_abspath,
> - svn_boolean_t dry_run,
> - int strip_count,
> - svn_boolean_t reverse,
> - svn_boolean_t ignore_whitespace,
> - svn_boolean_t remove_tempfiles,
> - svn_client_patch_func_t patch_func,
> - void *patch_baton,
> - svn_client_ctx_t *ctx,
> - apr_pool_t *scratch_pool)
> +svn_client_patch2(apr_file_t *apr_file,
> + const char *wc_dir_abspath,
> + svn_boolean_t dry_run,
> + int strip_count,
> + svn_boolean_t reverse,
> + svn_boolean_t ignore_whitespace,
> + svn_boolean_t remove_tempfiles,
> + svn_client_patch_func_t patch_func,
> + void *patch_baton,
> + svn_client_ctx_t *ctx,
> + apr_pool_t *scratch_pool)
> {
> svn_node_kind_t kind;
>
> @@ -3751,18 +3749,6 @@ svn_client_patch(const char *patch_abspa
> svn_dirent_local_style(wc_dir_abspath,
> scratch_pool));
>
> - SVN_ERR(svn_io_check_path(patch_abspath, &kind, scratch_pool));
> - if (kind == svn_node_none)
> - return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
> - _("'%s' does not exist"),
> - svn_dirent_local_style(patch_abspath,
> - scratch_pool));
> - if (kind != svn_node_file)
> - return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
> - _("'%s' is not a file"),
> - svn_dirent_local_style(patch_abspath,
> - scratch_pool));
> -
> SVN_ERR(svn_io_check_path(wc_dir_abspath, &kind, scratch_pool));
> if (kind == svn_node_none)
> return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
> @@ -3776,7 +3762,7 @@ svn_client_patch(const char *patch_abspa
> scratch_pool));
>
> SVN_WC__CALL_WITH_WRITE_LOCK(
> - apply_patches(patch_abspath, wc_dir_abspath, dry_run, strip_count,
> + apply_patches(apr_file, wc_dir_abspath, dry_run, strip_count,
> reverse, ignore_whitespace, remove_tempfiles,
> patch_func, patch_baton, ctx, scratch_pool),
> ctx->wc_ctx, wc_dir_abspath, FALSE /* lock_anchor */, scratch_pool);
>
> Modified: subversion/trunk/subversion/svn/patch-cmd.c
> URL:
> http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/patch-cmd.c?rev=1925463&r1=1925462&r2=1925463&view=diff
>
> ==============================================================================
> --- subversion/trunk/subversion/svn/patch-cmd.c (original)
> +++ subversion/trunk/subversion/svn/patch-cmd.c Thu May 8 14:19:32 2025
> @@ -51,8 +51,10 @@ svn_cl__patch(apr_getopt_t *os,
> apr_array_header_t *targets;
> const char *abs_patch_path;
> const char *patch_path;
> + apr_file_t *patch_file;
> const char *abs_target_path;
> const char *target_path;
> + svn_node_kind_t kind;
>
> opt_state = ((svn_cl__cmd_baton_t *)baton)->opt_state;
> ctx = ((svn_cl__cmd_baton_t *)baton)->ctx;
> @@ -74,6 +76,19 @@ svn_cl__patch(apr_getopt_t *os,
>
> SVN_ERR(svn_dirent_get_absolute(&abs_patch_path, patch_path, pool));
>
> + SVN_ERR(svn_io_check_path(abs_patch_path, &kind, pool));
> + if (kind == svn_node_none)
> + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
> + _("'%s' does not exist"),
> + svn_dirent_local_style(abs_patch_path,
> pool));
> + if (kind != svn_node_file)
> + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
> + _("'%s' is not a file"),
> + svn_dirent_local_style(abs_patch_path,
> pool));
> +
> + SVN_ERR(svn_io_file_open(&patch_file, abs_patch_path,
> + APR_READ | APR_BUFFERED, APR_OS_DEFAULT,
> pool));
> +
> if (targets->nelts == 1)
> target_path = ""; /* "" is the canonical form of "." */
> else
> @@ -84,12 +99,13 @@ svn_cl__patch(apr_getopt_t *os,
> }
> SVN_ERR(svn_dirent_get_absolute(&abs_target_path, target_path, pool));
>
> - SVN_ERR(svn_client_patch(abs_patch_path, abs_target_path,
> - opt_state->dry_run, opt_state->strip,
> - opt_state->reverse_diff,
> - opt_state->ignore_whitespace,
> - TRUE, NULL, NULL, ctx, pool));
> + SVN_ERR(svn_client_patch2(patch_file, abs_target_path,
> + opt_state->dry_run, opt_state->strip,
> + opt_state->reverse_diff,
> + opt_state->ignore_whitespace,
> + TRUE, NULL, NULL, ctx, pool));
>
> + SVN_ERR(svn_io_file_close(patch_file, pool));
>
> if (! opt_state->quiet)
> SVN_ERR(svn_cl__notifier_print_conflict_stats(ctx->notify_baton2,
> pool));
>
> 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=1925463&r1=1925462&r2=1925463&view=diff
>
> ==============================================================================
> --- subversion/trunk/subversion/tests/libsvn_client/client-test.c
> (original)
> +++ subversion/trunk/subversion/tests/libsvn_client/client-test.c Thu May
> 8 14:19:32 2025
> @@ -348,6 +348,8 @@ test_patch(const svn_test_opts_t *opts,
> const char *reject_tempfile_path;
> const char *key;
> int i;
> + apr_off_t off;
> +
> #define NL APR_EOL_STR
> #define UNIDIFF_LINES 7
> const char *unidiff_patch[UNIDIFF_LINES] = {
> @@ -401,8 +403,8 @@ test_patch(const svn_test_opts_t *opts,
> pool, svn_test_data_path("test-patch", pool),
> "test-patch.diff", SVN_VA_NULL);
> SVN_ERR(svn_io_file_open(&patch_file, patch_file_path,
> - (APR_READ | APR_WRITE | APR_CREATE |
> APR_TRUNCATE),
> - APR_OS_DEFAULT, pool));
> + (APR_READ | APR_WRITE | APR_CREATE |
> APR_TRUNCATE
> + | APR_BUFFERED), APR_OS_DEFAULT, pool));
> for (i = 0; i < UNIDIFF_LINES; i++)
> {
> apr_size_t len = strlen(unidiff_patch[i]);
> @@ -415,9 +417,12 @@ test_patch(const svn_test_opts_t *opts,
> pcb.patched_tempfiles = apr_hash_make(pool);
> pcb.reject_tempfiles = apr_hash_make(pool);
> pcb.state_pool = pool;
> - SVN_ERR(svn_client_patch(patch_file_path, wc_path, FALSE, 0, FALSE,
> - FALSE, FALSE, patch_collection_func, &pcb,
> - ctx, pool));
> +
> + off = 0;
> + SVN_ERR(svn_io_file_seek(patch_file, APR_SET, &off, pool));
> + SVN_ERR(svn_client_patch2(patch_file, wc_path, FALSE, 0, FALSE,
> + FALSE, FALSE, patch_collection_func, &pcb,
> + ctx, pool));
> SVN_ERR(svn_io_file_close(patch_file, pool));
>
> SVN_TEST_ASSERT(apr_hash_count(pcb.patched_tempfiles) == 1);
>
>
>
--
Timofei Zhakov