Hi! [[[ Fix issue #3483 - extend svn_client_upgrade() to include externals. I've done the externals upgrading after wc upgrade is finished. In that way no errors in the externals will affect the wc.
* subversion/libsvn_client/cleanup.c (svn_client_upgrade): Get all svn:externals. We need the target_dir so we have to parse the description. For each target_dir we call svn_wc_upgrade() which will recursively upgrade the external. * subversion/tests/cmdline/upgrade_tests.py (upgrade_with_externals): New. Checks the format of a 1.6 wc upgraded to wc-ng. (test_list): Add upgrade with_externals. * subversion/tests/cmdline/upgrade_tests_data/upgrade_with_extenals.tar.bz2 (...): New. An 1.6 wc with the same structure as those used in externals_tests.py. Patch by: Daniel Näslund <daniel{_AT_}longitudo.com> ]]] A big thank you to Bert for putting up with all my questions on IRC. My previous patch didn't handle relative externals all that well. And it failed to upgrade file externals too. I've added a testcase to this patch to prove that this one does things right. BUT, the testcase does not check the statustree of the wc after the upgrade. I didn't understand how to set up a statustree to check against. Daniel
Index: subversion/tests/cmdline/upgrade_tests.py =================================================================== --- subversion/tests/cmdline/upgrade_tests.py (revision 894319) +++ subversion/tests/cmdline/upgrade_tests.py (arbetskopia) @@ -139,7 +139,24 @@ expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1) run_and_verify_status_no_server(sbox.wc_dir, expected_status) +def upgrade_with_externals(sbox): + "upgrade with externals" + + # Create wc from tarfile, uses the same structure of the wc as the tests + # in externals_tests.py. + replace_sbox_with_tarfile(sbox, 'upgrade_with_externals.tar.bz2') + # Attempt to use the working copy, this should give an error + expected_stderr = wc_is_too_old_regex + svntest.actions.run_and_verify_svn(None, None, expected_stderr, + 'info', sbox.wc_dir) + # Now upgrade the working copy + svntest.actions.run_and_verify_svn(None, None, [], + 'upgrade', sbox.wc_dir) + + # Actually check the format number of the upgraded working copy + check_format(sbox, get_current_format()) + def upgrade_1_5_body(sbox, subcommand): replace_sbox_with_tarfile(sbox, 'upgrade_1_5.tar.bz2') @@ -216,6 +233,7 @@ # list all tests here, starting with None: test_list = [ None, basic_upgrade, + upgrade_with_externals, upgrade_1_5, XFail(update_1_5), logs_left_1_5, Index: subversion/tests/cmdline/upgrade_tests_data/upgrade_with_externals.tar.bz2 =================================================================== Kan inte visa: filen markerad som binär. svn:mime-type = application/octet-stream Egenskapsändringar för: subversion/tests/cmdline/upgrade_tests_data/upgrade_with_externals.tar.bz2 ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: subversion/libsvn_client/cleanup.c =================================================================== --- subversion/libsvn_client/cleanup.c (revision 894319) +++ subversion/libsvn_client/cleanup.c (arbetskopia) @@ -33,8 +33,11 @@ #include "svn_config.h" #include "svn_dirent_uri.h" #include "client.h" +#include "svn_pools.h" +#include "svn_props.h" #include "svn_private_config.h" +#include "private/svn_wc_private.h" /*** Code. ***/ @@ -62,6 +65,10 @@ apr_pool_t *scratch_pool) { const char *local_abspath; + apr_hash_t *externals; + apr_hash_index_t *hi; + apr_pool_t *iterpool; + svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}}; SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath, @@ -69,5 +76,78 @@ ctx->notify_func2, ctx->notify_baton2, scratch_pool)); + /* Now it's time to upgrade the externals too. We do it after the wc + upgrade to avoid that errors in the externals causes the wc upgrade to + fail. Thanks to caching the performance penalty of walking the wc a + second time shouldn't be too severe */ + SVN_ERR(svn_client_propget3(&externals, SVN_PROP_EXTERNALS, path, &rev, + &rev, NULL, svn_depth_infinity, NULL, ctx, + scratch_pool)); + + iterpool = svn_pool_create(scratch_pool); + + for (hi = apr_hash_first(scratch_pool, externals); hi; + hi = apr_hash_next(hi)) + { + const char *key; + int i; + apr_ssize_t klen; + svn_string_t *external_desc; + apr_array_header_t *externals_p; + + svn_pool_clear(iterpool); + externals_p = apr_array_make(iterpool, 1, + sizeof(svn_wc_external_item2_t*)); + + apr_hash_this(hi, (void*)&key, &klen, NULL); + + external_desc = apr_hash_get(externals, key, klen); + + SVN_ERR(svn_wc_parse_externals_description3(&externals_p, + svn_dirent_dirname(path, + iterpool), + external_desc->data, TRUE, + iterpool)); + for (i = 0; i < externals_p->nelts; i++) + { + svn_wc_external_item2_t *item; + const char *external_abspath; + const char *external_path; + svn_node_kind_t kind; + svn_error_t *err; + + item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*); + + /* The key is the path to the dir the svn:externals was set on */ + external_path = svn_dirent_join(key, item->target_dir, iterpool); + + SVN_ERR(svn_dirent_get_absolute(&external_abspath, external_path, + iterpool)); + + /* This is hack. We can only send dirs to svn_wc_upgrade(). This + way we will get an exception saying that the wc must be + upgraded if it's a dir. If it's a file then the lookup is done + in an adm_dir belonging to the real wc and since that was + updated before the externals no error is returned. */ + err = svn_wc__node_get_kind(&kind, ctx->wc_ctx, external_abspath, + FALSE, iterpool); + + if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) + { + SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, external_abspath, + ctx->cancel_func, ctx->cancel_baton, + ctx->notify_func2, ctx->notify_baton2, + iterpool)); + svn_error_clear(err); + } + else if (err) + return svn_error_return(err); + else + ; + } + } + + svn_pool_destroy(iterpool); + return SVN_NO_ERROR; }