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 <rin...@apache.org> 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

Reply via email to