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\

Reply via email to