Re: svn commit: r1925463 - in /subversion/trunk/subversion: include/svn_client.h libsvn_client/deprecated.c libsvn_client/patch.c svn/patch-cmd.c tests/libsvn_client/client-test.c

Branko Čibej <[email protected]>
Newsgroups gmane.comp.version-control.subversion.svn
Organization The Apache Software Foundation
Message-ID <[email protected]>
On 8. 5. 25 19:01, Timofei Zhakov wrote:
> 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?

Which specific error is returned is not, IMO, part of our compatibility 
guarantees. However:

[...]

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


*This* is clearly wrong. If the call to svn_client_patch2 fails, we leak 
the file descriptor. That doesn't affect the svn command-line much but 
it does affect users of the library. Looking at the whole commit, it 
appears that the same bug existed in apply_patches() before your change.

[...]

>     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
>     <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));
>     +
>

Here, you're duplicating code that you just implemented in the 
svn_client_patch() wrapper. That doesn't make sense. Instead, you could 
create a svn_client__patch() private helper function that's used both 
here and by the svn_client_patch() compatibility wrapper. Duplicating 
code is a bad idea, because ...

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

... you also duplicated the file descriptor leak.

[...]

>     @@ -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);
>

Same here, although this is a test, so I wouldn't bother fixing it.

The more I look at this change, the more I wonder if svn_client_patch() 
should be deprecated at all. It's clearly a useful bit of code, since we 
can use it in our command-line implementation. It's also far easier to 
use by third parties if they're not doing something complicated. There's 
no reason to not have both svn_client_patch() and svn_client_patch2() in 
the non-deprecated API.

-- Brane
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.