Julian Foad <jul...@foad.me.uk> writes: > Issue #4889 "per-WC config" is the subject of Johan's new dev@ post > "Pristines-on-demand=enabled == format 32?". We already concurred that > it's wise to decouple "pristines-on-demand mode is enabled in this WC" > from "the WC format is (at least) 32 so can support that mode". > <https://subversion.apache.org/issue/4889>. This may be considered > higher priority than fixing the remaining tests.
I have been thinking about this recently, and here is a patch with the first-cut implementation that persists the pristines-on-demand setting in a working copy. Unfortunately, I am getting a bit swamped with other things to complete the work on it, but perhaps it could be useful as a building block for the full implementation. The patch currently allows doing an `svn checkout --store-pristines=no`, which is going to create a working copy that doesn't store the pristine copies of the files and fetches them on demand. The setting is persisted in wc.db. The patch doesn't include the following: 1) An update for the tests and the test suite to run the tests in both modes. Personally, I think that we should update the test runner so that it would execute the tests for both pristine modes by default, without requiring any specific switches. Because otherwise, there is a chance that one of the equally supported core configurations may receive far less attention during the development and test runs. 2) An update for `svn info` to display the value of the new setting. 3) An ability to take the --store-pristines value from a user config, perhaps on a per-URL basis. While working on the patch, I have stumbled across a couple of issues: A) `svn upgrade` without arguments fails for a working copy with latest format $ svn checkout --compatible-version=1.15 wc $ svn upgrade wc $ svn: E155021: Working copy '…' is already at version 1.15 (format 32) and cannot be downgraded to version 1.8 (format 31) I haven't given it a lot of thought, but we might want to handle this case without an error or even think about making `svn upgrade` by default upgrade to the latest available version instead of the minimum supported (similar to `svnadmin upgrade`). B) Shelving and pristines-on-demand It seems that both v2 and v3 shelving implementations are currently not updated to support pristines-on-demand working copies. C) Bumping the related API This part originates from B). For example, v3 shelving uses the libsvn_wc APIs, such as svn_wc_revert6(). If the working copy is created without the pristine contents, those calls are going to fail with an error saying that there is no text-base for the corresponding path. This is a tricky error to understand, and the failure itself is unpredictable, because it depends on whether any of the previous API calls have fetched the missing text-bases. So if we think about v3 shelving as an example of the libsvn_wc API user, other existing third-party users of the API could face the same problem. Perhaps, we could bump the APIs that currently rely on the text-bases to always be available. And we could then make their deprecated versions fail (predictably) for working copies that don't store pristine contents. Thanks, Evgeny Kotkov
Index: subversion/include/private/svn_wc_private.h =================================================================== --- subversion/include/private/svn_wc_private.h (revision 1899956) +++ subversion/include/private/svn_wc_private.h (working copy) @@ -2231,9 +2231,11 @@ const svn_version_t * svn_wc__min_supported_format_version(void); /** - * Set @a format to the format of the nearest parent working copy root of - * @a local_abspath in @a wc_ctx, or to the oldest format of any root stored - * there. If @a wc_ctx is empty, return the library's default format. + * Set @a *format_p and @a *store_pristines_p to the settings of the + * nearest parent working copy root of @a local_abspath in @a wc_ctx, + * or to settings of any root stored there, preferring the one with + * the oldest format. If @a wc_ctx is empty, return the library's + * default settings. * * Use @a scratch_pool for temporary allocations. * @@ -2240,16 +2242,18 @@ svn_wc__min_supported_format_version(void); * @since New in 1.15. */ svn_error_t * -svn_wc__format_from_context(int *format, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool); +svn_wc__settings_from_context(int *format_p, + svn_boolean_t *store_pristines_p, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *scratch_pool); /** * Ensure that an administrative area exists for @a local_abspath, so that @a * local_abspath is a working copy subdir with schema version @a target_format - * based on @a url at @a revision, with depth @a depth, and with repository UUID - * @a repos_uuid and repository root URL @a repos_root_url. + * based on @a url at @a revision, with depth @a depth, with repository UUID + * @a repos_uuid and repository root URL @a repos_root_url, and with the + * @a store_pristines setting value. * * @a depth must be a definite depth, it cannot be #svn_depth_unknown. * @a repos_uuid and @a repos_root_url MUST NOT be @c NULL, and @@ -2280,11 +2284,13 @@ svn_wc__ensure_adm(svn_wc_context_t *wc_ctx, const char *repos_uuid, svn_revnum_t revision, svn_depth_t depth, + svn_boolean_t store_pristines, apr_pool_t *scratch_pool); /** - * Upgrade the working copy at @a local_abspath to the metadata - * storage format indicated by @a target_format. @a local_abspath + * Upgrade the working copy at @a local_abspath to the metadata storage + * format indicated by @a target_format. Use the @a store_pristines + * settings value for the upgraded working copy. @a local_abspath * should be an absolute path to the root of the working copy. * * If @a cancel_func is non-NULL, invoke it with @a cancel_baton at @@ -2306,6 +2312,7 @@ svn_error_t * svn_wc__upgrade(svn_wc_context_t *wc_ctx, const char *local_abspath, int target_format, + svn_boolean_t store_pristines, svn_wc_upgrade_get_repos_info_t repos_info_func, void *repos_info_baton, svn_cancel_func_t cancel_func, @@ -2341,6 +2348,15 @@ svn_wc__textbase_sync(svn_wc_context_t *wc_ctx, void *cancel_baton, apr_pool_t *scratch_pool); +/* Return the working copy settings *FORMAT_P and *STORE_PRISTINES_P for + LOCAL_ABSPATH in WC_CTX. */ +svn_error_t * +svn_wc__get_settings(int *format_p, + svn_boolean_t *store_pristines_p, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *scratch_pool); + #ifdef __cplusplus } #endif /* __cplusplus */ Index: subversion/include/svn_client.h =================================================================== --- subversion/include/svn_client.h (revision 1899956) +++ subversion/include/svn_client.h (working copy) @@ -1245,6 +1245,11 @@ svn_client_args_to_target_array(apr_array_header_t * @c NULL means the library's version. * See svn_client_default_wc_version(), * svn_client_get_wc_formats_supported(). + * @param[in] store_pristines If #svn_tristate_true, the pristine contents of + * all files in the working copy will be stored on disk. <br> + * If #svn_tristate_false, the pristine contents will be fetched + * on-demand when required by the operation. <br> + * If #svn_tristate_unknown, the default setting will be used. * @param[in] ctx The standard client context, used for authentication and * notification. * @param[in] pool Used for any temporary allocation. @@ -1274,6 +1279,7 @@ svn_client_checkout4(svn_revnum_t *result_rev, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, const svn_version_t *wc_format_version, + svn_tristate_t store_pristines, svn_client_ctx_t *ctx, apr_pool_t *pool); Index: subversion/include/svn_error_codes.h =================================================================== --- subversion/include/svn_error_codes.h (revision 1899956) +++ subversion/include/svn_error_codes.h (working copy) @@ -576,6 +576,11 @@ SVN_ERROR_START SVN_ERR_WC_CATEGORY_START + 41, "Duplicate targets in svn:externals property") + /** @since New in 1.15 */ + SVN_ERRDEF(SVN_ERR_WC_INCOMPATIBLE_SETTINGS, + SVN_ERR_WC_CATEGORY_START + 42, + "Incompatible working copy settings") + /* fs errors */ SVN_ERRDEF(SVN_ERR_FS_GENERAL, Index: subversion/libsvn_client/checkout.c =================================================================== --- subversion/libsvn_client/checkout.c (revision 1899956) +++ subversion/libsvn_client/checkout.c (working copy) @@ -38,8 +38,10 @@ #include "svn_io.h" #include "svn_opt.h" #include "svn_time.h" +#include "svn_version.h" #include "client.h" +#include "private/svn_subr_private.h" #include "private/svn_wc_private.h" #include "svn_private_config.h" @@ -52,6 +54,7 @@ initialize_area(int target_format, const char *local_abspath, const svn_client__pathrev_t *pathrev, svn_depth_t depth, + svn_boolean_t store_pristines, svn_client_ctx_t *ctx, apr_pool_t *pool) { @@ -62,7 +65,7 @@ initialize_area(int target_format, SVN_ERR(svn_wc__ensure_adm(ctx->wc_ctx, target_format, local_abspath, pathrev->url, pathrev->repos_root_url, pathrev->repos_uuid, - pathrev->rev, depth, pool)); + pathrev->rev, depth, store_pristines, pool)); return SVN_NO_ERROR; } @@ -78,11 +81,13 @@ svn_client__checkout_internal(svn_revnum_t *result svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, const svn_version_t *wc_format_version, + svn_tristate_t store_pristines, svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { int target_format; + svn_boolean_t target_store_pristines; svn_node_kind_t kind; svn_client__pathrev_t *pathrev; svn_opt_revision_t resolved_rev = { svn_opt_revision_number }; @@ -98,15 +103,28 @@ svn_client__checkout_internal(svn_revnum_t *result && (revision->kind != svn_opt_revision_head)) return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); - /* Here the default for wc_format_version is determined from WC context, - * rather than the library's default version. */ - if (wc_format_version) - SVN_ERR(svn_wc__format_from_version(&target_format, wc_format_version, - scratch_pool)); + if (wc_format_version == NULL && store_pristines == svn_tristate_unknown) + { + SVN_ERR(svn_wc__settings_from_context(&target_format, + &target_store_pristines, + ctx->wc_ctx, local_abspath, + scratch_pool)); + } else - SVN_ERR(svn_wc__format_from_context(&target_format, ctx->wc_ctx, - local_abspath, scratch_pool)); + { + SVN_ERR_ASSERT(wc_format_version != NULL); + SVN_ERR(svn_wc__format_from_version(&target_format, wc_format_version, + scratch_pool)); + + SVN_ERR_ASSERT(store_pristines != svn_tristate_unknown); + + if (store_pristines == svn_tristate_true) + target_store_pristines = TRUE; + else + target_store_pristines = FALSE; + } + /* Get the RA connection, if needed. */ if (ra_session) { @@ -158,7 +176,7 @@ svn_client__checkout_internal(svn_revnum_t *result URL, revnum, and an 'incomplete' flag. */ SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool)); SVN_ERR(initialize_area(target_format, local_abspath, pathrev, depth, - ctx, scratch_pool)); + target_store_pristines, ctx, scratch_pool)); } else if (kind == svn_node_dir) { @@ -171,10 +189,25 @@ svn_client__checkout_internal(svn_revnum_t *result if (! present_format) { SVN_ERR(initialize_area(target_format, local_abspath, pathrev, depth, - ctx, scratch_pool)); + target_store_pristines, ctx, scratch_pool)); } else { + svn_boolean_t wc_store_pristines; + + SVN_ERR(svn_wc__get_settings(NULL, &wc_store_pristines, ctx->wc_ctx, + local_abspath, scratch_pool)); + + if ((target_store_pristines && !wc_store_pristines) || + (!target_store_pristines && wc_store_pristines)) + { + return svn_error_createf( + SVN_ERR_WC_INCOMPATIBLE_SETTINGS, NULL, + _("'%s' is an existing working copy with different '%s' setting"), + svn_dirent_local_style(local_abspath, scratch_pool), + "store-pristines"); + } + /* Get PATH's URL. */ SVN_ERR(svn_wc__node_get_url(&entry_url, ctx->wc_ctx, local_abspath, scratch_pool, scratch_pool)); @@ -228,6 +261,7 @@ svn_client_checkout4(svn_revnum_t *result_rev, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, const svn_version_t *wc_format_version, + svn_tristate_t store_pristines, svn_client_ctx_t *ctx, apr_pool_t *pool) { @@ -237,10 +271,27 @@ svn_client_checkout4(svn_revnum_t *result_rev, SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); - /* A NULL wc_format_version translates to the default version. */ + if (store_pristines == svn_tristate_unknown) + store_pristines = svn_tristate_true; + + /* A NULL wc_format_version translates to the minimum compatible version. */ if (!wc_format_version) - wc_format_version = svn_client_default_wc_version(pool); + { + wc_format_version = svn_client_default_wc_version(pool); + if (store_pristines == svn_tristate_false) + { + const svn_version_t *required_version = + svn_client__compatible_wc_version_pristines_on_demand(pool); + + if (!svn_version__at_least(wc_format_version, + required_version->major, + required_version->minor, + required_version->patch)) + wc_format_version = required_version; + } + } + err = svn_client__checkout_internal(result_rev, &sleep_here, URL, local_abspath, peg_revision, revision, depth, @@ -247,6 +298,7 @@ svn_client_checkout4(svn_revnum_t *result_rev, ignore_externals, allow_unver_obstructions, wc_format_version, + store_pristines, NULL /* ra_session */, ctx, pool); if (sleep_here) Index: subversion/libsvn_client/client.h =================================================================== --- subversion/libsvn_client/client.h (revision 1899956) +++ subversion/libsvn_client/client.h (working copy) @@ -540,10 +540,18 @@ svn_client__update_internal(svn_revnum_t *result_r to fail. A new working copy, if needed, will be created in the format corresponding - to the WC_FORMAT_VERSION of the client. If this parameter is NULL, the - format will be determined from context (see svn_wc__format_from_context). - The format of any existing working copy will remain unchanged. + to the WC_FORMAT_VERSION of the client. The format of any existing working + copy will remain unchanged. + If STORE_PRISTINES is svn_tristate_true, the pristine contents of all + files in the working copy will be stored on disk. If STORE_PRISTINES is + svn_tristate_false, the pristine contents will be fetched on-demand when + required by the operation. + + If WC_FORMAT_VERSION is NULL and STORE_PRISTINES is svn_tristate_unknown, the + settings will be determined from context (see svn_wc__settings_from_context). + Otherwise, both WC_FORMAT_VERSION and STORE_PRISTINES must be defined. + If RA_SESSION is NOT NULL, it may be used to avoid creating a new session. The session may point to a different URL after returning. */ @@ -558,6 +566,7 @@ svn_client__checkout_internal(svn_revnum_t *result svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, const svn_version_t *wc_format_version, + svn_tristate_t store_pristines, svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *pool); @@ -1251,6 +1260,11 @@ svn_client__textbase_sync(const char *local_abspat svn_client_ctx_t *ctx, apr_pool_t *scratch_pool); +/* Returns the first version that supported the working copy metadata format + * where pristines can be fetched on demand. */ +const svn_version_t * +svn_client__compatible_wc_version_pristines_on_demand(apr_pool_t *result_pool); + #ifdef __cplusplus } #endif /* __cplusplus */ Index: subversion/libsvn_client/copy.c =================================================================== --- subversion/libsvn_client/copy.c (revision 1899956) +++ subversion/libsvn_client/copy.c (working copy) @@ -2488,6 +2488,7 @@ svn_client__repos_to_wc_copy_dir(svn_boolean_t *ti TRUE /*ignore_externals*/, FALSE, /* we don't allow obstructions */ NULL, /* default WC format */ + svn_tristate_unknown, ra_session, ctx, scratch_pool); ctx->notify_func2 = old_notify_func2; Index: subversion/libsvn_client/deprecated.c =================================================================== --- subversion/libsvn_client/deprecated.c (revision 1899956) +++ subversion/libsvn_client/deprecated.c (working copy) @@ -2683,7 +2683,8 @@ svn_client_checkout3(svn_revnum_t *result_rev, return svn_error_trace(svn_client_checkout4( result_rev, URL, path, peg_revision, revision, depth, - ignore_externals, FALSE, NULL, ctx, pool)); + ignore_externals, FALSE, NULL, + svn_tristate_unknown, ctx, pool)); } svn_error_t * Index: subversion/libsvn_client/externals.c =================================================================== --- subversion/libsvn_client/externals.c (revision 1899956) +++ subversion/libsvn_client/externals.c (working copy) @@ -413,6 +413,7 @@ switch_dir_external(const char *local_abspath, revision, svn_depth_infinity, FALSE, FALSE, NULL, /* default WC format */ + svn_tristate_unknown, ra_session, ctx, pool)); Index: subversion/libsvn_client/shelf.c =================================================================== --- subversion/libsvn_client/shelf.c (revision 1899956) +++ subversion/libsvn_client/shelf.c (working copy) @@ -1034,6 +1034,7 @@ shelf_copy_base(svn_client__shelf_version_t *new_s TRUE /*ignore_externals*/, FALSE /*allow_unver_obstructions*/, NULL, /* default WC format */ + svn_tristate_unknown, ra_session, ctx, scratch_pool)); /* ### hopefully we won't eventually need to sleep_here... */ Index: subversion/libsvn_client/upgrade.c =================================================================== --- subversion/libsvn_client/upgrade.c (revision 1899956) +++ subversion/libsvn_client/upgrade.c (working copy) @@ -93,6 +93,7 @@ static svn_error_t * upgrade_externals_from_properties(svn_client_ctx_t *ctx, const char *local_abspath, int wc_format, + svn_boolean_t store_pristines, struct repos_info_baton *info_baton, apr_pool_t *scratch_pool); @@ -99,6 +100,7 @@ upgrade_externals_from_properties(svn_client_ctx_t static svn_error_t * upgrade_internal(const char *path, int wc_format, + svn_boolean_t store_pristines, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { @@ -116,7 +118,8 @@ upgrade_internal(const char *path, _("'%s' is not a local path"), path); SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); - SVN_ERR(svn_wc__upgrade(ctx->wc_ctx, local_abspath, wc_format, + SVN_ERR(svn_wc__upgrade(ctx->wc_ctx, local_abspath, + wc_format, store_pristines, fetch_repos_info, &info_baton, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, @@ -155,7 +158,8 @@ upgrade_internal(const char *path, if (kind == svn_node_dir) { svn_error_t *err = upgrade_internal(ext_abspath, wc_format, - ctx, iterpool); + store_pristines, ctx, + iterpool); if (err) { @@ -179,7 +183,8 @@ upgrade_internal(const char *path, /* Upgrading from <= 1.6, or no svn:properties defined. (There is no way to detect the difference from libsvn_client :( ) */ - SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath, wc_format, + SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath, + wc_format, store_pristines, &info_baton, scratch_pool)); } @@ -200,7 +205,7 @@ svn_client_upgrade2(const char *path, SVN_ERR(svn_wc__format_from_version(&wc_format, wc_format_version, scratch_pool)); - SVN_ERR(upgrade_internal(path, wc_format, ctx, scratch_pool)); + SVN_ERR(upgrade_internal(path, wc_format, TRUE, ctx, scratch_pool)); return SVN_NO_ERROR; } @@ -269,10 +274,21 @@ svn_client_latest_wc_version(apr_pool_t *result_po return &version; } +const svn_version_t * +svn_client__compatible_wc_version_pristines_on_demand(apr_pool_t *result_pool) +{ + /* NOTE: For consistency, always return the version of the client + that first introduced the format. */ + static const svn_version_t version = { 1, 15, 0, NULL }; + return &version; +} + /* Helper for upgrade_externals_from_properties: upgrades one external ITEM in EXTERNALS_PARENT. Uses SCRATCH_POOL for temporary allocations. */ static svn_error_t * -upgrade_external_item(svn_client_ctx_t *ctx, int wc_format, +upgrade_external_item(svn_client_ctx_t *ctx, + int wc_format, + svn_boolean_t store_pristines, const char *externals_parent_abspath, const char *externals_parent_url, const char *externals_parent_repos_root_url, @@ -315,7 +331,8 @@ static svn_error_t * { svn_error_clear(err); - SVN_ERR(upgrade_internal(external_abspath, wc_format, ctx, scratch_pool)); + SVN_ERR(upgrade_internal(external_abspath, wc_format, store_pristines, + ctx, scratch_pool)); } else if (err) return svn_error_trace(err); @@ -389,6 +406,7 @@ static svn_error_t * upgrade_externals_from_properties(svn_client_ctx_t *ctx, const char *local_abspath, int wc_format, + svn_boolean_t store_pristines, struct repos_info_baton *info_baton, apr_pool_t *scratch_pool) { @@ -477,7 +495,7 @@ upgrade_externals_from_properties(svn_client_ctx_t item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*); svn_pool_clear(inner_iterpool); - err = upgrade_external_item(ctx, wc_format, + err = upgrade_external_item(ctx, wc_format, store_pristines, externals_parent_abspath, externals_parent_url, externals_parent_repos_root_url, Index: subversion/libsvn_wc/adm_files.c =================================================================== --- subversion/libsvn_wc/adm_files.c (revision 1899956) +++ subversion/libsvn_wc/adm_files.c (working copy) @@ -207,6 +207,7 @@ init_adm(svn_wc__db_t *db, const char *repos_uuid, svn_revnum_t initial_rev, svn_depth_t depth, + svn_boolean_t store_pristines, apr_pool_t *pool) { /* First, make an empty administrative area. */ @@ -228,7 +229,7 @@ init_adm(svn_wc__db_t *db, /* Create the SDB. */ SVN_ERR(svn_wc__db_init(db, target_format, local_abspath, repos_relpath, repos_root_url, repos_uuid, - initial_rev, depth, pool)); + initial_rev, depth, store_pristines, pool)); /* Stamp ENTRIES and FORMAT files for old clients. */ SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath, @@ -254,6 +255,7 @@ svn_wc__internal_ensure_adm(svn_wc__db_t *db, const char *repos_uuid, svn_revnum_t revision, svn_depth_t depth, + svn_boolean_t store_pristines, apr_pool_t *scratch_pool) { int present_format; @@ -265,6 +267,7 @@ svn_wc__internal_ensure_adm(svn_wc__db_t *db, svn_wc__db_status_t status; const char *db_repos_relpath, *db_repos_root_url, *db_repos_uuid; svn_revnum_t db_revision; + svn_boolean_t wc_store_pristines; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); SVN_ERR_ASSERT(url != NULL); @@ -272,6 +275,24 @@ svn_wc__internal_ensure_adm(svn_wc__db_t *db, SVN_ERR_ASSERT(repos_uuid != NULL); SVN_ERR_ASSERT(repos_relpath != NULL); + if (target_format < SVN_WC__SUPPORTED_VERSION) + return svn_error_createf( + SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, + _("Working copy format %d is not supported by client version %s."), + target_format, SVN_VER_NUM); + + if (target_format > SVN_WC__VERSION) + return svn_error_createf( + SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, + _("Working copy format %d can't be created by client version %s."), + target_format, SVN_VER_NUM); + + if (target_format < SVN_WC__PRISTINES_ON_DEMAND_VERSION && !store_pristines) + return svn_error_createf( + SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, + _("Working copy format %d does not support the requested capabilities"), + target_format); + SVN_ERR(svn_wc__internal_check_wc(&present_format, db, local_abspath, TRUE, scratch_pool)); @@ -279,24 +300,32 @@ svn_wc__internal_ensure_adm(svn_wc__db_t *db, just create one. */ if (present_format == 0) { - - if (target_format < SVN_WC__SUPPORTED_VERSION) - return svn_error_createf( - SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, - _("Working copy format %d is not supported by client version %s."), - target_format, SVN_VER_NUM); - - if (target_format > SVN_WC__VERSION) - return svn_error_createf( - SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, - _("Working copy format %d can't be created by client version %s."), - target_format, SVN_VER_NUM); - return svn_error_trace(init_adm(db, target_format, local_abspath, repos_relpath, repos_root_url, repos_uuid, - revision, depth, scratch_pool)); + revision, depth, store_pristines, + scratch_pool)); } + else if (present_format != target_format) + { + return svn_error_createf( + SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, + _("Format %d doesn't match existing format %d in '%s'"), + target_format, present_format, local_abspath); + } + SVN_ERR(svn_wc__db_get_settings(NULL, &wc_store_pristines, db, + local_abspath, scratch_pool)); + + if ((store_pristines && !wc_store_pristines) || + (!store_pristines && wc_store_pristines)) + { + return svn_error_createf( + SVN_ERR_WC_INCOMPATIBLE_SETTINGS, NULL, + _("'%s' is an existing working copy with different '%s' setting"), + svn_dirent_local_style(local_abspath, scratch_pool), + "store-pristines"); + } + SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, &db_repos_relpath, &db_repos_root_url, &db_repos_uuid, @@ -314,13 +343,6 @@ svn_wc__internal_ensure_adm(svn_wc__db_t *db, if (status != svn_wc__db_status_deleted && status != svn_wc__db_status_not_present) { - /* Check that the existing format matches the requested format. */ - if (present_format != target_format) - return svn_error_createf( - SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, - _("Format %d doesn't match existing format %d in '%s'"), - target_format, present_format, local_abspath); - /* ### Should we match copyfrom_revision? */ if (db_revision != revision) return @@ -389,12 +411,13 @@ svn_wc__ensure_adm(svn_wc_context_t *wc_ctx, const char *repos_uuid, svn_revnum_t revision, svn_depth_t depth, + svn_boolean_t store_pristines, apr_pool_t *scratch_pool) { return svn_error_trace( svn_wc__internal_ensure_adm(wc_ctx->db, target_format, local_abspath, url, repos_root_url, repos_uuid, revision, - depth, scratch_pool)); + depth, store_pristines, scratch_pool)); } svn_error_t * Index: subversion/libsvn_wc/deprecated.c =================================================================== --- subversion/libsvn_wc/deprecated.c (revision 1899956) +++ subversion/libsvn_wc/deprecated.c (working copy) @@ -582,7 +582,7 @@ svn_wc_ensure_adm4(svn_wc_context_t *wc_ctx, return svn_error_trace( svn_wc__ensure_adm(wc_ctx, SVN_WC__DEFAULT_VERSION, local_abspath, url, repos_root_url, repos_uuid, revision, depth, - scratch_pool)); + TRUE, scratch_pool)); } svn_error_t * @@ -4900,7 +4900,7 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx, void *notify_baton, apr_pool_t *scratch_pool) { - return svn_wc__upgrade(wc_ctx, local_abspath, SVN_WC__DEFAULT_VERSION, + return svn_wc__upgrade(wc_ctx, local_abspath, SVN_WC__DEFAULT_VERSION, TRUE, repos_info_func, repos_info_baton, cancel_func, cancel_baton, notify_func, notify_baton, Index: subversion/libsvn_wc/info.c =================================================================== --- subversion/libsvn_wc/info.c (revision 1899956) +++ subversion/libsvn_wc/info.c (working copy) @@ -106,8 +106,8 @@ build_info_for_node(svn_wc__info2_t **info, wc_info->copyfrom_rev = SVN_INVALID_REVNUM; - SVN_ERR(svn_wc__db_get_format(&wc_info->wc_format, - db, local_abspath, scratch_pool)); + SVN_ERR(svn_wc__db_get_settings(&wc_info->wc_format, NULL, + db, local_abspath, scratch_pool)); SVN_ERR(svn_wc__db_read_info(&status, &db_kind, &tmpinfo->rev, &repos_relpath, @@ -556,3 +556,16 @@ svn_wc__get_info(svn_wc_context_t *wc_ctx, return SVN_NO_ERROR; } + +svn_error_t * +svn_wc__get_settings(int *format_p, + svn_boolean_t *store_pristines_p, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *scratch_pool) +{ + SVN_ERR(svn_wc__db_get_settings(format_p, store_pristines_p, wc_ctx->db, + local_abspath, scratch_pool)); + + return SVN_NO_ERROR; +} Index: subversion/libsvn_wc/textbase.c =================================================================== --- subversion/libsvn_wc/textbase.c (revision 1899956) +++ subversion/libsvn_wc/textbase.c (working copy) @@ -527,14 +527,14 @@ svn_wc__textbase_sync(svn_wc_context_t *wc_ctx, void *cancel_baton, apr_pool_t *scratch_pool) { - const char *mode; + svn_boolean_t store_pristines; textbase_sync_baton_t baton = {0}; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - SVN_ERR(svn_wc__db_pristines_mode(&mode, wc_ctx->db, local_abspath, - scratch_pool)); - if (strcmp(mode, "local-only") == 0) + SVN_ERR(svn_wc__db_get_settings(NULL, &store_pristines, wc_ctx->db, + local_abspath, scratch_pool)); + if (store_pristines) return SVN_NO_ERROR; baton.db = wc_ctx->db; Index: subversion/libsvn_wc/upgrade.c =================================================================== --- subversion/libsvn_wc/upgrade.c (revision 1899956) +++ subversion/libsvn_wc/upgrade.c (working copy) @@ -2026,6 +2026,7 @@ svn_error_t * svn_wc__upgrade(svn_wc_context_t *wc_ctx, const char *local_abspath, int target_format, + svn_boolean_t store_pristines, svn_wc_upgrade_get_repos_info_t repos_info_func, void *repos_info_baton, svn_cancel_func_t cancel_func, @@ -2128,7 +2129,7 @@ svn_wc__upgrade(svn_wc_context_t *wc_ctx, &data.repos_id, &data.wc_id, db, target_format, data.root_abspath, this_dir->repos, this_dir->uuid, - scratch_pool)); + store_pristines, scratch_pool)); /* Migrate the entries over to the new database. ### We need to think about atomicity here. Index: subversion/libsvn_wc/wc-metadata.sql =================================================================== --- subversion/libsvn_wc/wc-metadata.sql (revision 1899956) +++ subversion/libsvn_wc/wc-metadata.sql (working copy) @@ -713,7 +713,8 @@ WHERE l.op_depth = 0 following schema changes: - Add the 'hydrated' column to the PRISTINE table. - Add the I_PRISTINE_UNREFERENCED index. - - Add the TEXTBASE_REFS table. */ + - Add the TEXTBASE_REFS table. + - Add the SETTINGS table. */ -- STMT_UPGRADE_TO_32 /* True iff the pristine contents are currently available on disk. */ ALTER TABLE PRISTINE ADD COLUMN hydrated INTEGER NOT NULL DEFAULT 1; @@ -778,6 +779,12 @@ BEGIN AND op_depth = OLD.op_depth; END; +/* This table contains settings of a working copy, identified by WC_ID. */ +CREATE TABLE SETTINGS ( + wc_id INTEGER NOT NULL REFERENCES WCROOT (id) PRIMARY KEY, + store_pristines INTEGER +); + PRAGMA user_version = 32; /* ------------------------------------------------------------------------- */ Index: subversion/libsvn_wc/wc-queries.sql =================================================================== --- subversion/libsvn_wc/wc-queries.sql (revision 1899956) +++ subversion/libsvn_wc/wc-queries.sql (working copy) @@ -1896,6 +1896,14 @@ UNION ALL SELECT pristine.checksum, pristine.hydrated, 0, NULL, NULL, NULL FROM pristine WHERE refcount = 0 +-- STMT_SELECT_SETTINGS +SELECT store_pristines FROM settings WHERE wc_id = ?1 + +-- STMT_UPSERT_SETTINGS +INSERT INTO settings (wc_id, store_pristines) +VALUES (?1, ?2) +ON CONFLICT(wc_id) DO UPDATE SET store_pristines=?2 + /* ------------------------------------------------------------------------- */ /* Grab all the statements related to the schema. */ Index: subversion/libsvn_wc/wc.h =================================================================== --- subversion/libsvn_wc/wc.h (revision 1899956) +++ subversion/libsvn_wc/wc.h (working copy) @@ -223,6 +223,9 @@ extern "C" { * demand. */ #define SVN_WC__PRISTINES_ON_DEMAND_VERSION 32 +/* Starting from this version, the DB stores per-WC settings. */ +#define SVN_WC__SETTINGS_VERSION 32 + /* Return a string indicating the released version (or versions) of * Subversion that used WC format number WC_FORMAT, or some other * suitable string if no released version used WC_FORMAT. @@ -538,6 +541,7 @@ svn_wc__internal_ensure_adm(svn_wc__db_t *db, const char *repos_uuid, svn_revnum_t revision, svn_depth_t depth, + svn_boolean_t store_pristines, apr_pool_t *scratch_pool); Index: subversion/libsvn_wc/wc_db.c =================================================================== --- subversion/libsvn_wc/wc_db.c (revision 1899956) +++ subversion/libsvn_wc/wc_db.c (working copy) @@ -1366,6 +1366,7 @@ init_db(/* output values */ const char *root_node_repos_relpath, svn_revnum_t root_node_revision, svn_depth_t root_node_depth, + svn_boolean_t store_pristines, const char *wcroot_abspath, apr_pool_t *scratch_pool) { @@ -1389,6 +1390,13 @@ init_db(/* output values */ SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT)); SVN_ERR(svn_sqlite__insert(wc_id, stmt)); + if (target_format >= SVN_WC__SETTINGS_VERSION) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPSERT_SETTINGS)); + SVN_ERR(svn_sqlite__bindf(stmt, "id", *wc_id, store_pristines)); + SVN_ERR(svn_sqlite__insert(NULL, stmt)); + } + if (root_node_repos_relpath) { svn_wc__db_status_t status = svn_wc__db_status_normal; @@ -1436,6 +1444,7 @@ create_db(svn_sqlite__db_t **sdb, const char *root_node_repos_relpath, svn_revnum_t root_node_revision, svn_depth_t root_node_depth, + svn_boolean_t store_pristines, svn_boolean_t exclusive, apr_int32_t timeout, apr_pool_t *result_pool, @@ -1450,7 +1459,8 @@ create_db(svn_sqlite__db_t **sdb, SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id, *sdb, target_format, repos_root_url, repos_uuid, root_node_repos_relpath, root_node_revision, - root_node_depth, dir_abspath, scratch_pool), + root_node_depth, store_pristines, dir_abspath, + scratch_pool), *sdb); return SVN_NO_ERROR; @@ -1466,6 +1476,7 @@ svn_wc__db_init(svn_wc__db_t *db, const char *repos_uuid, svn_revnum_t initial_rev, svn_depth_t depth, + svn_boolean_t store_pristines, apr_pool_t *scratch_pool) { svn_sqlite__db_t *sdb; @@ -1495,7 +1506,7 @@ svn_wc__db_init(svn_wc__db_t *db, /* Create the SDB and insert the basic rows. */ SVN_ERR(create_db(&sdb, &repos_id, &wc_id, target_format, local_abspath, repos_root_url, repos_uuid, SDB_FILE, - repos_relpath, initial_rev, depth, + repos_relpath, initial_rev, depth, store_pristines, sqlite_exclusive, sqlite_timeout, db->state_pool, scratch_pool)); @@ -1504,6 +1515,7 @@ svn_wc__db_init(svn_wc__db_t *db, apr_pstrdup(db->state_pool, local_abspath), sdb, wc_id, FORMAT_FROM_SDB, FALSE /* auto-upgrade */, + store_pristines, db->state_pool, scratch_pool)); /* Any previously cached children may now have a new WCROOT, most likely that @@ -1537,10 +1549,11 @@ svn_wc__db_init(svn_wc__db_t *db, svn_error_t * -svn_wc__db_get_format(int *format, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool) +svn_wc__db_get_settings(int *format_p, + svn_boolean_t *store_pristines_p, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; @@ -1552,7 +1565,11 @@ svn_error_t * local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - *format = wcroot->format; + if (format_p) + *format_p = wcroot->format; + if (store_pristines_p) + *store_pristines_p = wcroot->store_pristines; + return SVN_NO_ERROR; } @@ -13442,6 +13459,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, const char *dir_abspath, const char *repos_root_url, const char *repos_uuid, + svn_boolean_t store_pristines, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; @@ -13451,6 +13469,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, dir_abspath, repos_root_url, repos_uuid, SDB_FILE, NULL, SVN_INVALID_REVNUM, svn_depth_unknown, + store_pristines, TRUE /* exclusive */, 0 /* timeout */, wc_db->state_pool, scratch_pool)); @@ -13460,6 +13479,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, dir_abspath), *sdb, *wc_id, FORMAT_FROM_SDB, FALSE /* auto-upgrade */, + store_pristines, wc_db->state_pool, scratch_pool)); /* The WCROOT is complete. Stash it into DB. */ Index: subversion/libsvn_wc/wc_db.h =================================================================== --- subversion/libsvn_wc/wc_db.h (revision 1899956) +++ subversion/libsvn_wc/wc_db.h (working copy) @@ -310,17 +310,20 @@ svn_wc__db_init(svn_wc__db_t *db, const char *repos_uuid, svn_revnum_t initial_rev, svn_depth_t depth, + svn_boolean_t store_pristines, apr_pool_t *scratch_pool); -/* Return the working copy format for LOCAL_ABSPATH in DB in *FORMAT. +/* Return the working copy settings *FORMAT_P and *STORE_PRISTINES_P for + LOCAL_ABSPATH in DB. Use SCRATCH_POOL for temporary allocations. */ svn_error_t * -svn_wc__db_get_format(int *format, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool); +svn_wc__db_get_settings(int *format_p, + svn_boolean_t *store_pristines_p, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool); /* Compute the LOCAL_RELPATH for the given LOCAL_ABSPATH, relative from wri_abspath. @@ -1086,14 +1089,6 @@ svn_wc__db_pristine_dehydrate(svn_wc__db_t *db, const svn_checksum_t *sha1_checksum, apr_pool_t *scratch_pool); -/* Return the *PRISTINES_MODE for the WC at (DB, LOCAL_ABSPATH). - */ -svn_error_t * -svn_wc__db_pristines_mode(const char **pristines_mode, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool); - /* @defgroup svn_wc__db_external External management @{ */ @@ -2988,6 +2983,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, const char *local_dir_abspath, const char *repos_root_url, const char *repos_uuid, + svn_boolean_t store_pristines, apr_pool_t *scratch_pool); /* Simply insert (or replace) one row in the EXTERNALS table. */ Index: subversion/libsvn_wc/wc_db_pristine.c =================================================================== --- subversion/libsvn_wc/wc_db_pristine.c (revision 1899956) +++ subversion/libsvn_wc/wc_db_pristine.c (working copy) @@ -418,7 +418,7 @@ svn_wc__db_pristine_prepare_install(svn_stream_t * install_data = apr_pcalloc(result_pool, sizeof(*install_data)); install_data->wcroot = wcroot; - if (hydrated || strcmp(wcroot->pristines_mode, "local-only") == 0) + if (hydrated || wcroot->store_pristines) { SVN_ERR_W(svn_stream__create_for_install(&install_data->inner_stream, temp_dir_abspath, Index: subversion/libsvn_wc/wc_db_private.h =================================================================== --- subversion/libsvn_wc/wc_db_private.h (revision 1899956) +++ subversion/libsvn_wc/wc_db_private.h (working copy) @@ -107,8 +107,9 @@ typedef struct svn_wc__db_wcroot_t { const char *local_abspath -> svn_wc_adm_access_t *adm_access */ apr_hash_t *access_cache; - /* How to manage the pristines ("local-only" or "on-demand") */ - const char *pristines_mode; + /* Whether to store the pristine contents of all files on disk or + to fetch the contents on demand. */ + svn_boolean_t store_pristines; } svn_wc__db_wcroot_t; @@ -129,6 +130,7 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t * apr_int64_t wc_id, int format, svn_boolean_t verify_format, + svn_boolean_t store_pristines, apr_pool_t *result_pool, apr_pool_t *scratch_pool); Index: subversion/libsvn_wc/wc_db_wcroot.c =================================================================== --- subversion/libsvn_wc/wc_db_wcroot.c (revision 1899956) +++ subversion/libsvn_wc/wc_db_wcroot.c (working copy) @@ -303,6 +303,7 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t * apr_int64_t wc_id, int format, svn_boolean_t verify_format, + svn_boolean_t store_pristines, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -390,12 +391,7 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t * (*wcroot)->owned_locks = apr_array_make(result_pool, 8, sizeof(svn_wc__db_wclock_t)); (*wcroot)->access_cache = apr_hash_make(result_pool); - /* Determine the pristines-mode for this WC. */ - /* ### TODO: read this from per-WC storage instead of locked to wc format */ - if (format < SVN_WC__PRISTINES_ON_DEMAND_VERSION) - (*wcroot)->pristines_mode = "local-only"; - else - (*wcroot)->pristines_mode = "on-demand"; + (*wcroot)->store_pristines = store_pristines; /* SDB will be NULL for pre-NG working copies. We only need to run a cleanup when the SDB is present. */ @@ -502,6 +498,38 @@ verify_stats_table(svn_sqlite__db_t *sdb, return SVN_NO_ERROR; } +/* Read and return the settings for WC_ID in SDB. */ +static svn_error_t * +read_settings(svn_boolean_t *store_pristines_p, + svn_sqlite__db_t *sdb, + int format, + apr_int64_t wc_id, + apr_pool_t *scratch_pool) +{ + if (format >= SVN_WC__SETTINGS_VERSION) + { + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_SETTINGS)); + SVN_ERR(svn_sqlite__bindf(stmt, "i", wc_id)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + *store_pristines_p = svn_sqlite__column_boolean(stmt, 0); + else + *store_pristines_p = TRUE; + + SVN_ERR(svn_sqlite__reset(stmt)); + } + else + { + *store_pristines_p = TRUE; + } + + return SVN_NO_ERROR; +} + /* Sqlite transaction helper for opening the db in svn_wc__db_wcroot_parse_local_abspath() to avoid multiple db operations that each obtain and release a lock */ @@ -508,6 +536,7 @@ verify_stats_table(svn_sqlite__db_t *sdb, static svn_error_t * fetch_sdb_info(apr_int64_t *wc_id, int *format, + svn_boolean_t *store_pristines, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) { @@ -518,7 +547,7 @@ fetch_sdb_info(apr_int64_t *wc_id, svn_wc__db_util_fetch_wc_id(wc_id, sdb, scratch_pool), svn_sqlite__read_schema_version(format, sdb, scratch_pool), verify_stats_table(sdb, *format, scratch_pool), - SVN_NO_ERROR, + read_settings(store_pristines, sdb, *format, *wc_id, scratch_pool), sdb); return SVN_NO_ERROR; @@ -771,9 +800,10 @@ try_symlink_as_dir: apr_int64_t wc_id; int format; + svn_boolean_t store_pristines; svn_error_t *err; - err = fetch_sdb_info(&wc_id, &format, sdb, scratch_pool); + err = fetch_sdb_info(&wc_id, &format, &store_pristines, sdb, scratch_pool); if (err) { if (err->apr_err == SVN_ERR_WC_CORRUPT) @@ -794,6 +824,7 @@ try_symlink_as_dir: : local_abspath), sdb, wc_id, format, db->verify_format, + store_pristines, db->state_pool, scratch_pool); if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT || err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) && @@ -869,6 +900,7 @@ try_symlink_as_dir: : local_abspath), NULL, UNKNOWN_WC_ID, wc_format, db->verify_format, + TRUE, db->state_pool, scratch_pool)); } @@ -1047,10 +1079,11 @@ svn_wc__db_drop_root(svn_wc__db_t *db, * TODO: Convert the svn_wc__db_t::dir_data hash to a sorted dictionary?. */ svn_error_t * -svn_wc__format_from_context(int *format, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool) +svn_wc__settings_from_context(int *format_p, + svn_boolean_t *store_pristines_p, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *scratch_pool) { apr_hash_t *const dir_data = wc_ctx->db->dir_data; apr_array_header_t *keys; @@ -1060,7 +1093,8 @@ svn_error_t * SVN_ERR(svn_hash_keys(&keys, dir_data, scratch_pool)); if (0 == keys->nelts) { - *format = SVN_WC__DEFAULT_VERSION; + *format_p = SVN_WC__DEFAULT_VERSION; + *store_pristines_p = TRUE; return SVN_NO_ERROR; } @@ -1082,7 +1116,8 @@ svn_error_t * { /* Found an exact match in the WC context. */ svn_wc__db_wcroot_t *wcroot = svn_hash_gets(dir_data, here); - *format = wcroot->format; + *format_p = wcroot->format; + *store_pristines_p = wcroot->store_pristines; return SVN_NO_ERROR; } } @@ -1094,7 +1129,8 @@ svn_error_t * { /* Found the parent path in the WC context. */ svn_wc__db_wcroot_t *wcroot = svn_hash_gets(dir_data, prev); - *format = wcroot->format; + *format_p = wcroot->format; + *store_pristines_p = wcroot->store_pristines; return SVN_NO_ERROR; } } @@ -1103,6 +1139,7 @@ svn_error_t * /* Find the oldest format recorded in the WC context. */ { int oldest_format = SVN_WC__VERSION; + svn_boolean_t store_pristines = TRUE; apr_hash_index_t *hi; for (hi = apr_hash_first(scratch_pool, dir_data); @@ -1111,28 +1148,14 @@ svn_error_t * { svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); if (wcroot->format < oldest_format) - oldest_format = wcroot->format; + { + oldest_format = wcroot->format; + store_pristines = wcroot->store_pristines; + } } - *format = oldest_format; + *format_p = oldest_format; + *store_pristines_p = store_pristines; return SVN_NO_ERROR; } } - -svn_error_t * -svn_wc__db_pristines_mode(const char **pristines_mode, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, - local_abspath, scratch_pool, - scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - - *pristines_mode = wcroot->pristines_mode; - return SVN_NO_ERROR; -} Index: subversion/svn/checkout-cmd.c =================================================================== --- subversion/svn/checkout-cmd.c (revision 1899956) +++ subversion/svn/checkout-cmd.c (working copy) @@ -173,6 +173,7 @@ svn_cl__checkout(apr_getopt_t *os, opt_state->ignore_externals, opt_state->force, opt_state->compatible_version, + opt_state->store_pristines, ctx, subpool)); } svn_pool_destroy(subpool); Index: subversion/svn/cl.h =================================================================== --- subversion/svn/cl.h (revision 1899956) +++ subversion/svn/cl.h (working copy) @@ -276,6 +276,7 @@ typedef struct svn_cl__opt_state_t svn_cl__viewspec_svn11 } viewspec; /* value of --x-viewspec */ svn_version_t *compatible_version; /* working copy compatibility version */ + svn_boolean_t store_pristines; } svn_cl__opt_state_t; /* Conflict stats for operations such as update and merge. */ @@ -376,6 +377,7 @@ typedef enum svn_cl__longopt_t { opt_drop, opt_viewspec, opt_compatible_version, + opt_store_pristines } svn_cl__longopt_t; /* Options for giving a log message. (Some of these also have other uses.) Index: subversion/svn/svn.c =================================================================== --- subversion/svn/svn.c (revision 1899956) +++ subversion/svn/svn.c (working copy) @@ -371,6 +371,17 @@ const apr_getopt_option_t svn_cl__options[] = " " "version ARG (\"1.8\", \"1.9.5\", etc.)")}, + {"store-pristines", opt_store_pristines, 1, + N_("Configure the working copy to either store local\n" + " " + "copies of pristine contents ('yes') or to fetch\n" + " " + "them on demand ('no'). Fetching on demand saves\n" + " " + "disk space, but may require network access for\n" + " " + "commands such as diff or revert. Default: 'yes'.")}, + /* Long-opt Aliases * * These have NULL descriptions, but an option code that matches some @@ -543,7 +554,7 @@ svn_cl__cmd_table_main[] = " reporting the action taken.\n" )}, {'r', 'q', 'N', opt_depth, opt_force, opt_ignore_externals, - opt_compatible_version}, + opt_compatible_version, opt_store_pristines}, {{'N', N_("obsolete; same as --depth=files")}} }, { "cleanup", svn_cl__cleanup, {0}, {N_( @@ -2178,6 +2189,7 @@ sub_main(int *exit_code, int argc, const char *arg opt_state.accept_which = svn_cl__accept_unspecified; opt_state.show_revs = svn_cl__show_revs_invalid; opt_state.file_size_unit = SVN_CL__SIZE_UNIT_NONE; + opt_state.store_pristines = svn_tristate_unknown; /* No args? Show usage. */ if (argc <= 1) @@ -2734,6 +2746,15 @@ sub_main(int *exit_code, int argc, const char *arg case opt_compatible_version: SVN_ERR(parse_compatible_version(&opt_state, opt_arg, pool)); break; + case opt_store_pristines: + SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool)); + opt_state.store_pristines = svn_tristate__from_word(utf8_opt_arg); + if (opt_state.store_pristines == svn_tristate_unknown) + return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Unknown value '%s' for %s.\n" + "Supported values: %s"), + utf8_opt_arg, "--store-pristines", "yes, no"); + break; default: /* Hmmm. Perhaps this would be a good place to squirrel away opts that commands like svn diff might need. Hmmm indeed. */ Index: subversion/tests/libsvn_client/client-test.c =================================================================== --- subversion/tests/libsvn_client/client-test.c (revision 1899956) +++ subversion/tests/libsvn_client/client-test.c (working copy) @@ -392,7 +392,9 @@ test_patch(const svn_test_opts_t *opts, SVN_ERR(svn_client_checkout4(NULL, repos_url, wc_path, &peg_rev, &rev, svn_depth_infinity, TRUE, FALSE, - opts->wc_format_version, ctx, pool)); + opts->wc_format_version, + opts->store_pristines, + ctx, pool)); /* Create the patch file. */ patch_file_path = svn_dirent_join_many( @@ -466,13 +468,17 @@ test_wc_add_scenarios(const svn_test_opts_t *opts, /* Checkout greek tree as wc_path */ SVN_ERR(svn_client_checkout4(NULL, repos_url, wc_path, &peg_rev, &rev, svn_depth_infinity, FALSE, FALSE, - opts->wc_format_version, ctx, pool)); + opts->wc_format_version, + opts->store_pristines, + ctx, pool)); /* Now checkout again as wc_path/NEW */ new_dir_path = svn_dirent_join(wc_path, "NEW", pool); SVN_ERR(svn_client_checkout4(NULL, repos_url, new_dir_path, &peg_rev, &rev, svn_depth_infinity, FALSE, FALSE, - opts->wc_format_version, ctx, pool)); + opts->wc_format_version, + opts->store_pristines, + ctx, pool)); ex_dir_path = svn_dirent_join(wc_path, "NEW_add", pool); ex2_dir_path = svn_dirent_join(wc_path, "NEW_add2", pool); @@ -632,7 +638,9 @@ test_16k_add(const svn_test_opts_t *opts, SVN_ERR(svn_client_checkout4(NULL, repos_url, wc_path, &peg_rev, &rev, svn_depth_infinity, TRUE, FALSE, - opts->wc_format_version, ctx, pool)); + opts->wc_format_version, + opts->store_pristines, + ctx, pool)); for (i = 0; i < 16384; i++) { @@ -763,7 +771,9 @@ test_foreign_repos_copy(const svn_test_opts_t *opt SVN_ERR(svn_client_checkout4(NULL, repos_url, wc_path, &peg_rev, &rev, svn_depth_infinity, FALSE, FALSE, - opts->wc_format_version, ctx, pool)); + opts->wc_format_version, + opts->store_pristines, + ctx, pool)); SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, repos2_url, NULL, &peg_rev, &rev, @@ -832,7 +842,9 @@ test_suggest_mergesources(const svn_test_opts_t *o wc_path, &head_rev, &head_rev, svn_depth_empty, FALSE, FALSE, - opts->wc_format_version, ctx, pool)); + opts->wc_format_version, + opts->store_pristines, + ctx, pool)); SVN_ERR(svn_client_suggest_merge_sources(&results, wc_path, @@ -979,7 +991,9 @@ test_remote_only_status(const svn_test_opts_t *opt apr_pstrcat(pool, repos_url, "/A", SVN_VA_NULL), wc_path, &rev, &rev, svn_depth_immediates, FALSE, FALSE, - opts->wc_format_version, ctx, pool)); + opts->wc_format_version, + opts->store_pristines, + ctx, pool)); /* Add a local file; this is a double-check to make sure that remote-only status ignores local changes. */ Index: subversion/tests/libsvn_client/conflicts-test.c =================================================================== --- subversion/tests/libsvn_client/conflicts-test.c (revision 1899956) +++ subversion/tests/libsvn_client/conflicts-test.c (working copy) @@ -6117,7 +6117,9 @@ test_file_vs_dir_move_merge_assertion_failure(cons "A1", pool), wc_path, &peg_rev, &opt_rev, svn_depth_infinity, TRUE, FALSE, - opts->wc_format_version, ctx, pool)); + opts->wc_format_version, + opts->store_pristines, + ctx, pool)); SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A", pool), Index: subversion/tests/libsvn_wc/entries-compat.c =================================================================== --- subversion/tests/libsvn_wc/entries-compat.c (revision 1899956) +++ subversion/tests/libsvn_wc/entries-compat.c (working copy) @@ -644,7 +644,7 @@ test_access_baton_like_locking(const svn_test_opts svn_path_url_add_component2(url, "sub-wc", pool), repos_root_url, repos_uuid, 0, svn_depth_infinity, - pool)); + opts->store_pristines, pool)); SVN_ERR(svn_wc__db_is_switched(&is_root, NULL, NULL, wc_ctx->db, subdir, pool)); Index: subversion/tests/libsvn_wc/utils.c =================================================================== --- subversion/tests/libsvn_wc/utils.c (revision 1899956) +++ subversion/tests/libsvn_wc/utils.c (working copy) @@ -108,6 +108,7 @@ create_repos_and_wc(const char **repos_url, FALSE /* ignore_externals */, FALSE /* allow_unver_obstructions */, opts->wc_format_version, + opts->store_pristines, ctx, subpool)); svn_pool_destroy(subpool); } Index: subversion/tests/libsvn_wc/wc-queries-test.c =================================================================== --- subversion/tests/libsvn_wc/wc-queries-test.c (revision 1899956) +++ subversion/tests/libsvn_wc/wc-queries-test.c (working copy) @@ -256,6 +256,8 @@ stmt_matches_wc_format(int stmt_num, case STMT_TEXTBASE_REMOVE_REF: case STMT_TEXTBASE_WALK: case STMT_TEXTBASE_SYNC: + case STMT_SELECT_SETTINGS: + case STMT_UPSERT_SETTINGS: return (wc_format >= 32); } return TRUE; Index: subversion/tests/svn_test.h =================================================================== --- subversion/tests/svn_test.h (revision 1899956) +++ subversion/tests/svn_test.h (working copy) @@ -219,6 +219,7 @@ typedef struct svn_test_opts_t /* WC format version to use for all tests (except tests for a specific format) */ const svn_version_t *wc_format_version; svn_boolean_t verbose; + svn_tristate_t store_pristines; /* Add future "arguments" here. */ } svn_test_opts_t; Index: subversion/tests/svn_test_main.c =================================================================== --- subversion/tests/svn_test_main.c (revision 1899956) +++ subversion/tests/svn_test_main.c (working copy) @@ -809,6 +809,7 @@ svn_test_main(int argc, const char *argv[], int ma opts.fs_type = DEFAULT_FS_TYPE; opts.wc_format_version = svn_wc__min_supported_format_version(); + opts.store_pristines = svn_tristate_unknown; /* Initialize APR (Apache pools) */ if (apr_initialize() != APR_SUCCESS) Index: tools/client-side/bash_completion =================================================================== --- tools/client-side/bash_completion (revision 1899956) +++ tools/client-side/bash_completion (working copy) @@ -882,7 +882,7 @@ _svn() ;; checkout|co) cmdOpts="$rOpts $qOpts $nOpts $pOpts --ignore-externals \ - --force" + --force --store-pristines" ;; cleanup) cmdOpts="$pOpts --include-externals -q --quiet\