You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2022/02/10 20:04:43 UTC

svn commit: r1897946 [2/3] - in /subversion/branches/pristines-on-demand-on-mwf: ./ build/ subversion/bindings/swig/python/tests/ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_wc/ subversion/tests/cmdline/ subversion/tests/cmd...

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/diff_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/diff_editor.c?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/diff_editor.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/diff_editor.c Thu Feb 10 20:04:43 2022
@@ -75,6 +75,7 @@
 #include "adm_files.h"
 #include "translate.h"
 #include "diff.h"
+#include "textbase.h"
 
 #include "svn_private_config.h"
 
@@ -476,15 +477,17 @@ svn_wc__diff_base_working_diff(svn_wc__d
   if (skip)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
-                                       db, local_abspath, checksum,
-                                       scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_setaside(&pristine_file,
+                                    db, local_abspath, checksum,
+                                    cancel_func, cancel_baton,
+                                    scratch_pool, scratch_pool));
 
   if (diff_pristine)
-    SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
-                                         db, local_abspath,
-                                         working_checksum,
-                                         scratch_pool, scratch_pool));
+    SVN_ERR(svn_wc__textbase_setaside(&local_file,
+                                      db, local_abspath,
+                                      working_checksum,
+                                      cancel_func, cancel_baton,
+                                      scratch_pool, scratch_pool));
   else if (! (had_props || props_mod))
     local_file = local_abspath;
   else if (files_same)
@@ -1019,8 +1022,9 @@ svn_wc__diff_local_only_file(svn_wc__db_
     right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
 
   if (checksum)
-    SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
-                                         checksum, scratch_pool, scratch_pool));
+    SVN_ERR(svn_wc__textbase_setaside(&pristine_file, db, local_abspath,
+                                      checksum, cancel_func, cancel_baton,
+                                      scratch_pool, scratch_pool));
   else
     pristine_file = NULL;
 
@@ -1415,9 +1419,10 @@ svn_wc__diff_base_only_file(svn_wc__db_t
   if (skip)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
-                                       db, local_abspath, checksum,
-                                       scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_setaside(&pristine_file,
+                                    db, local_abspath, checksum,
+                                    NULL, NULL,
+                                    scratch_pool, scratch_pool));
 
   SVN_ERR(processor->file_deleted(relpath,
                                   left_src,
@@ -2111,17 +2116,17 @@ apply_textdelta(void *file_baton,
                                                pool));
         }
 
-      SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
-                                       eb->db, fb->local_abspath,
-                                       fb->base_checksum,
-                                       pool, pool));
+      SVN_ERR(svn_wc__textbase_get_contents(&source,
+                                            eb->db, fb->local_abspath,
+                                            fb->base_checksum, FALSE,
+                                            pool, pool));
     }
   else if (fb->base_checksum)
     {
-      SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
-                                       eb->db, fb->local_abspath,
-                                       fb->base_checksum,
-                                       pool, pool));
+      SVN_ERR(svn_wc__textbase_get_contents(&source,
+                                            eb->db, fb->local_abspath,
+                                            fb->base_checksum, FALSE,
+                                            pool, pool));
     }
   else
     source = svn_stream_empty(pool);
@@ -2215,10 +2220,11 @@ close_file(void *file_baton,
     if (! repos_file)
       {
         assert(fb->base_checksum);
-        SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
-                                             eb->db, eb->anchor_abspath,
-                                             fb->base_checksum,
-                                             scratch_pool, scratch_pool));
+        SVN_ERR(svn_wc__textbase_setaside(&repos_file,
+                                          eb->db, fb->local_abspath,
+                                          fb->base_checksum,
+                                          eb->cancel_func, eb->cancel_baton,
+                                          scratch_pool, scratch_pool));
       }
   }
 
@@ -2251,10 +2257,11 @@ close_file(void *file_baton,
                                                 eb->db, fb->local_abspath,
                                                 scratch_pool, scratch_pool));
           assert(checksum);
-          SVN_ERR(svn_wc__db_pristine_get_path(&localfile,
-                                               eb->db, eb->anchor_abspath,
-                                               checksum,
-                                               scratch_pool, scratch_pool));
+          SVN_ERR(svn_wc__textbase_setaside(&localfile,
+                                            eb->db, fb->local_abspath,
+                                            checksum,
+                                            eb->cancel_func, eb->cancel_baton,
+                                            scratch_pool, scratch_pool));
         }
       else
         {

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/externals.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/externals.c?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/externals.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/externals.c Thu Feb 10 20:04:43 2022
@@ -53,6 +53,7 @@
 #include "translate.h"
 #include "workqueue.h"
 #include "conflicts.h"
+#include "textbase.h"
 
 #include "svn_private_config.h"
 
@@ -631,19 +632,21 @@ apply_textdelta(void *file_baton,
                                                            pool)));
         }
 
-      SVN_ERR(svn_wc__db_pristine_read(&src_stream, NULL, eb->db,
-                                       eb->wri_abspath, eb->original_checksum,
-                                       pool, pool));
+      SVN_ERR(svn_wc__textbase_get_contents(&src_stream, eb->db,
+                                            eb->local_abspath,
+                                            eb->original_checksum,
+                                            FALSE, pool, pool));
     }
   else
     src_stream = svn_stream_empty(pool);
 
-  SVN_ERR(svn_wc__db_pristine_prepare_install(&dest_stream,
-                                              &eb->install_data,
-                                              &eb->new_sha1_checksum,
-                                              &eb->new_md5_checksum,
-                                              eb->db, eb->wri_abspath,
-                                              eb->pool, pool));
+  SVN_ERR(svn_wc__textbase_prepare_install(&dest_stream,
+                                           &eb->install_data,
+                                           &eb->new_sha1_checksum,
+                                           &eb->new_md5_checksum,
+                                           eb->db, eb->local_abspath,
+                                           TRUE,
+                                           eb->pool, pool));
 
   svn_txdelta_apply(src_stream, dest_stream, NULL, eb->local_abspath, pool,
                     handler, handler_baton);
@@ -887,13 +890,43 @@ close_file(void *file_baton,
           }
         if (install_pristine)
           {
+            svn_stream_t *contents;
+            const char *tmpdir_abspath;
+            svn_stream_t *tmpstream;
+            const char *tmpfile_abspath;
+
+            SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, eb->db,
+                                             eb->wri_abspath,
+                                             eb->new_sha1_checksum,
+                                             pool, pool));
+            if (!contents)
+              return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
+                                      NULL, NULL);
+
+            SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath,
+                                                   eb->db, eb->wri_abspath,
+                                                   pool, pool));
+            SVN_ERR(svn_stream_open_unique(&tmpstream, &tmpfile_abspath,
+                                           tmpdir_abspath,
+                                           svn_io_file_del_none,
+                                           pool, pool));
+            SVN_ERR(svn_stream_copy3(contents, tmpstream, eb->cancel_func,
+                                     eb->cancel_baton, pool));
+
             SVN_ERR(svn_wc__wq_build_file_install(&work_item, eb->db,
                                             eb->local_abspath,
-                                            NULL,
+                                            tmpfile_abspath,
                                             eb->use_commit_times, TRUE,
                                             pool, pool));
 
             all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
+
+            SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
+                                                 eb->wri_abspath,
+                                                 tmpfile_abspath,
+                                                 pool, pool));
+
+            all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
           }
       }
     else

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/questions.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/questions.c?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/questions.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/questions.c Thu Feb 10 20:04:43 2022
@@ -79,15 +79,14 @@
 
 
 /* Set *MODIFIED_P to TRUE if (after translation) VERSIONED_FILE_ABSPATH
- * (of VERSIONED_FILE_SIZE bytes) differs from PRISTINE_STREAM (of
- * PRISTINE_SIZE bytes), else to FALSE if not.
+ * (of VERSIONED_FILE_SIZE bytes) differs from pristine file with checksum
+ * PRISTINE_CHECKSUM, else to FALSE if not.
  *
  * If EXACT_COMPARISON is FALSE, translate VERSIONED_FILE_ABSPATH's EOL
  * style and keywords to repository-normal form according to its properties,
- * and compare the result with PRISTINE_STREAM.  If EXACT_COMPARISON is
- * TRUE, translate PRISTINE_STREAM's EOL style and keywords to working-copy
- * form according to VERSIONED_FILE_ABSPATH's properties, and compare the
- * result with VERSIONED_FILE_ABSPATH.
+ * calculate checksum and compare the result with PRISTINE_STREAM.
+ * If EXACT_COMPARISON is TRUE, also check that VERSIONED_FILE_ABSPATH
+ * contents remains the same when retranslated according to its properties.
  *
  * HAS_PROPS should be TRUE if the file had properties when it was not
  * modified, otherwise FALSE.
@@ -95,8 +94,6 @@
  * PROPS_MOD should be TRUE if the file's properties have been changed,
  * otherwise FALSE.
  *
- * PRISTINE_STREAM will be closed before a successful return.
- *
  * DB is a wc_db; use SCRATCH_POOL for temporary allocation.
  */
 static svn_error_t *
@@ -104,20 +101,20 @@ compare_and_verify(svn_boolean_t *modifi
                    svn_wc__db_t *db,
                    const char *versioned_file_abspath,
                    svn_filesize_t versioned_file_size,
-                   svn_stream_t *pristine_stream,
-                   svn_filesize_t pristine_size,
+                   const svn_checksum_t *pristine_checksum,
                    svn_boolean_t has_props,
                    svn_boolean_t props_mod,
                    svn_boolean_t exact_comparison,
                    apr_pool_t *scratch_pool)
 {
-  svn_boolean_t same;
   svn_subst_eol_style_t eol_style;
   const char *eol_str;
   apr_hash_t *keywords;
   svn_boolean_t special = FALSE;
   svn_boolean_t need_translation;
   svn_stream_t *v_stream; /* versioned_file */
+  svn_checksum_t *v_checksum;
+  svn_error_t *err;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_file_abspath));
 
@@ -133,6 +130,9 @@ compare_and_verify(svn_boolean_t *modifi
                                          !exact_comparison,
                                          scratch_pool, scratch_pool));
 
+      if (eol_style == svn_subst_eol_style_unknown)
+        return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
+
       need_translation = svn_subst_translation_required(eol_style, eol_str,
                                                         keywords, special,
                                                         TRUE);
@@ -140,13 +140,20 @@ compare_and_verify(svn_boolean_t *modifi
   else
     need_translation = FALSE;
 
-  if (! need_translation
-      && (versioned_file_size != pristine_size))
+  if (! need_translation)
     {
-      *modified_p = TRUE;
+      svn_filesize_t pristine_size;
+
+      SVN_ERR(svn_wc__db_pristine_read(NULL, &pristine_size, db,
+                                       versioned_file_abspath, pristine_checksum,
+                                       scratch_pool, scratch_pool));
+
+      if (versioned_file_size != pristine_size)
+        {
+          *modified_p = TRUE;
 
-      /* ### Why did we open the pristine? */
-      return svn_error_trace(svn_stream_close(pristine_stream));
+          return SVN_NO_ERROR;
+        }
     }
 
   /* ### Other checks possible? */
@@ -162,46 +169,97 @@ compare_and_verify(svn_boolean_t *modifi
       /* We don't use APR-level buffering because the comparison function
        * will do its own buffering. */
       apr_file_t *file;
-      SVN_ERR(svn_io_file_open(&file, versioned_file_abspath, APR_READ,
-                               APR_OS_DEFAULT, scratch_pool));
+      err = svn_io_file_open(&file, versioned_file_abspath, APR_READ,
+                             APR_OS_DEFAULT, scratch_pool);
+      /* Convert EACCESS on working copy path to WC specific error code. */
+      if (err && APR_STATUS_IS_EACCES(err->apr_err))
+        return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
+      else
+        SVN_ERR(err);
       v_stream = svn_stream_from_aprfile2(file, FALSE, scratch_pool);
 
       if (need_translation)
         {
-          if (!exact_comparison)
+          const char *pristine_eol_str;
+
+          if (eol_style == svn_subst_eol_style_native)
+            pristine_eol_str = SVN_SUBST_NATIVE_EOL_STR;
+          else
+            pristine_eol_str = eol_str;
+
+          if (exact_comparison)
             {
-              if (eol_style == svn_subst_eol_style_native)
-                eol_str = SVN_SUBST_NATIVE_EOL_STR;
-              else if (eol_style != svn_subst_eol_style_fixed
-                       && eol_style != svn_subst_eol_style_none)
-                return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL,
-                                        svn_stream_close(v_stream), NULL);
+              svn_checksum_t *working_checksum;
+              svn_checksum_t *detranslated_checksum;
+              svn_checksum_t *retranslated_checksum;
+
+              v_stream = svn_stream_checksummed2(v_stream,
+                                                 &working_checksum, NULL,
+                                                 pristine_checksum->kind, TRUE,
+                                                 scratch_pool);
+
+              v_stream = svn_subst_stream_translated(v_stream,
+                                                     pristine_eol_str, TRUE,
+                                                     keywords, FALSE,
+                                                     scratch_pool);
+              v_stream = svn_stream_checksummed2(v_stream,
+                                                 &detranslated_checksum, NULL,
+                                                 pristine_checksum->kind, TRUE,
+                                                 scratch_pool);
+
+              v_stream = svn_subst_stream_translated(v_stream, eol_str, FALSE,
+                                                     keywords, TRUE,
+                                                     scratch_pool);
+              v_stream = svn_stream_checksummed2(v_stream,
+                                                 &retranslated_checksum, NULL,
+                                                 pristine_checksum->kind, TRUE,
+                                                 scratch_pool);
+
+              err = svn_stream_copy3(v_stream, svn_stream_empty(scratch_pool),
+                                     NULL, NULL, scratch_pool);
+              /* Convert EACCESS on working copy path to WC specific error code. */
+              if (err && APR_STATUS_IS_EACCES(err->apr_err))
+                return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
+              else
+                SVN_ERR(err);
+
+              if (svn_checksum_match(detranslated_checksum, pristine_checksum) &&
+                  svn_checksum_match(working_checksum, retranslated_checksum))
+                {
+                  *modified_p = FALSE;
+                }
+              else
+                {
+                  *modified_p = TRUE;
+                }
 
+              return SVN_NO_ERROR;
+            }
+          else
+            {
               /* Wrap file stream to detranslate into normal form,
                * "repairing" the EOL style if it is inconsistent. */
               v_stream = svn_subst_stream_translated(v_stream,
-                                                     eol_str,
+                                                     pristine_eol_str,
                                                      TRUE /* repair */,
                                                      keywords,
                                                      FALSE /* expand */,
                                                      scratch_pool);
             }
-          else
-            {
-              /* Wrap base stream to translate into working copy form, and
-               * arrange to throw an error if its EOL style is inconsistent. */
-              pristine_stream = svn_subst_stream_translated(pristine_stream,
-                                                            eol_str, FALSE,
-                                                            keywords, TRUE,
-                                                            scratch_pool);
-            }
         }
     }
 
-  SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream,
-                                    scratch_pool));
+  /* Get checksum of detranslated (normalized) content. */
+  err = svn_stream_contents_checksum(&v_checksum, v_stream,
+                                     pristine_checksum->kind,
+                                     scratch_pool, scratch_pool);
+  /* Convert EACCESS on working copy path to WC specific error code. */
+  if (err && APR_STATUS_IS_EACCES(err->apr_err))
+    return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
+  else
+    SVN_ERR(err);
 
-  *modified_p = (! same);
+  *modified_p = (! svn_checksum_match(v_checksum, pristine_checksum));
 
   return SVN_NO_ERROR;
 }
@@ -213,8 +271,6 @@ svn_wc__internal_file_modified_p(svn_boo
                                  svn_boolean_t exact_comparison,
                                  apr_pool_t *scratch_pool)
 {
-  svn_stream_t *pristine_stream;
-  svn_filesize_t pristine_size;
   svn_wc__db_status_t status;
   svn_node_kind_t kind;
   const svn_checksum_t *checksum;
@@ -302,27 +358,12 @@ svn_wc__internal_file_modified_p(svn_boo
     }
 
  compare_them:
-  SVN_ERR(svn_wc__db_pristine_read(&pristine_stream, &pristine_size,
-                                   db, local_abspath, checksum,
-                                   scratch_pool, scratch_pool));
-
   /* Check all bytes, and verify checksum if requested. */
-  {
-    svn_error_t *err;
-    err = compare_and_verify(modified_p, db,
+  SVN_ERR(compare_and_verify(modified_p, db,
                              local_abspath, dirent->filesize,
-                             pristine_stream, pristine_size,
-                             has_props, props_mod,
+                             checksum, has_props, props_mod,
                              exact_comparison,
-                             scratch_pool);
-
-    /* At this point we already opened the pristine file, so we know that
-       the access denied applies to the working copy path */
-    if (err && APR_STATUS_IS_EACCES(err->apr_err))
-      return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
-    else
-      SVN_ERR(err);
-  }
+                             scratch_pool));
 
   if (!*modified_p)
     {

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/revert.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/revert.c?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/revert.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/revert.c Thu Feb 10 20:04:43 2022
@@ -42,6 +42,7 @@
 #include "wc.h"
 #include "adm_files.h"
 #include "workqueue.h"
+#include "textbase.h"
 
 #include "svn_private_config.h"
 #include "private/svn_io_private.h"
@@ -714,10 +715,21 @@ revert_wc_data(svn_boolean_t *run_wq,
       if (kind == svn_node_file)
         {
           svn_skel_t *work_item;
+          const char *install_from;
+          svn_skel_t *cleanup_work_item;
 
+          SVN_ERR(svn_wc__textbase_setaside_wq(&install_from,
+                                               &cleanup_work_item,
+                                               db, local_abspath, NULL,
+                                               cancel_func, cancel_baton,
+                                               scratch_pool, scratch_pool));
           SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, local_abspath,
-                                                NULL, use_commit_times, TRUE,
+                                                install_from,
+                                                use_commit_times, TRUE,
                                                 scratch_pool, scratch_pool));
+          work_item = svn_wc__wq_merge(work_item, cleanup_work_item,
+                                       scratch_pool);
+
           SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_item,
                                     scratch_pool));
           *run_wq = TRUE;

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/update_editor.c?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/update_editor.c Thu Feb 10 20:04:43 2022
@@ -48,6 +48,7 @@
 #include "conflicts.h"
 #include "translate.h"
 #include "workqueue.h"
+#include "textbase.h"
 
 #include "private/svn_subr_private.h"
 #include "private/svn_wc_private.h"
@@ -439,7 +440,8 @@ struct handler_baton
 
 /* Get an empty file in the temporary area for WRI_ABSPATH.  The file will
    not be set for automatic deletion, and the name will be returned in
-   TMP_FILENAME.
+   TMP_FILENAME_P.  Set *CLEANUP_WORK_ITEM_P to a new work item that will
+   remove the temporary file.
 
    This implementation creates a new empty file with a unique name.
 
@@ -451,19 +453,35 @@ struct handler_baton
    ### file name to create later.  A better way may not be readily available.
  */
 static svn_error_t *
-get_empty_tmp_file(const char **tmp_filename,
+get_empty_tmp_file(const char **tmp_filename_p,
+                   svn_skel_t **cleanup_work_item_p,
                    svn_wc__db_t *db,
                    const char *wri_abspath,
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
 {
   const char *temp_dir_abspath;
+  const char *tmp_filename;
+  svn_skel_t *work_item;
+  svn_error_t *err;
 
   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
                                          scratch_pool, scratch_pool));
-  SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
+  SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_filename, temp_dir_abspath,
                                    svn_io_file_del_none,
                                    scratch_pool, scratch_pool));
+  err = svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
+                                     tmp_filename,
+                                     result_pool, scratch_pool);
+  if (err)
+    {
+      return svn_error_compose_create(
+               err,
+               svn_io_remove_file2(tmp_filename, TRUE, scratch_pool));
+    }
+
+  *tmp_filename_p = tmp_filename;
+  *cleanup_work_item_p = work_item;
 
   return SVN_NO_ERROR;
 }
@@ -3726,10 +3744,10 @@ lazy_open_source(svn_stream_t **stream,
 {
   struct file_baton *fb = baton;
 
-  SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
-                                   fb->local_abspath,
-                                   fb->original_checksum,
-                                   result_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_get_contents(stream, fb->edit_baton->db,
+                                        fb->local_abspath,
+                                        fb->original_checksum, FALSE,
+                                        result_pool, scratch_pool));
 
 
   return SVN_NO_ERROR;
@@ -3748,19 +3766,35 @@ lazy_open_target(svn_stream_t **stream_p
   svn_stream_t *pristine_install_stream;
   svn_wc__working_file_writer_t *file_writer;
   svn_stream_t *stream;
+  svn_boolean_t hydrated;
+
+  if (fb->shadowed || fb->obstruction_found || fb->edit_obstructed)
+    {
+      hydrated = TRUE;
+    }
+  else if (fb->adding_file && !fb->add_existed)
+    {
+      /* Clean new file, hint that we don't need the text-base. */
+      hydrated = FALSE;
+    }
+  else
+    {
+      hydrated = TRUE;
+    }
 
   /* By convention return value is undefined on error, but we rely
      on HB->INSTALL_DATA value in window_handler() and abort
      INSTALL_STREAM if is not NULL on error.
      So we store INSTALL_DATA to local variable first, to leave
      HB->INSTALL_DATA unchanged on error. */
-  SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_install_stream,
-                                              &pristine_install_data,
-                                              &hb->new_text_base_sha1_checksum,
-                                              NULL,
-                                              fb->edit_baton->db,
-                                              fb->dir_baton->local_abspath,
-                                              result_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_prepare_install(&pristine_install_stream,
+                                           &pristine_install_data,
+                                           &hb->new_text_base_sha1_checksum,
+                                           NULL,
+                                           fb->edit_baton->db,
+                                           fb->local_abspath,
+                                           hydrated,
+                                           result_pool, scratch_pool));
 
   if (fb->shadowed || fb->obstruction_found || fb->edit_obstructed)
     {
@@ -4062,17 +4096,19 @@ svn_wc__perform_file_merge(svn_skel_t **
      the textual changes into the working file. */
   const char *oldrev_str, *newrev_str, *mine_str;
   const char *merge_left;
-  svn_boolean_t delete_left = FALSE;
   const char *path_ext = "";
   const char *new_pristine_abspath;
   enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
   svn_skel_t *work_item;
+  svn_skel_t *cleanup_queue = NULL;
 
   *work_items = NULL;
 
-  SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
-                                       db, wri_abspath, new_checksum,
-                                       scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_setaside_wq(&new_pristine_abspath, &work_item,
+                                       db, local_abspath, new_checksum,
+                                       cancel_func, cancel_baton,
+                                       result_pool, scratch_pool));
+  cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool);
 
   /* If we have any file extensions we're supposed to
      preserve in generated conflict file names, then find
@@ -4106,14 +4142,19 @@ svn_wc__perform_file_merge(svn_skel_t **
 
   if (! original_checksum)
     {
-      SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
+      SVN_ERR(get_empty_tmp_file(&merge_left, &work_item, db, wri_abspath,
                                  result_pool, scratch_pool));
-      delete_left = TRUE;
+      cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool);
     }
   else
-    SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
-                                         original_checksum,
-                                         result_pool, scratch_pool));
+    {
+      SVN_ERR(svn_wc__textbase_setaside_wq(&merge_left, &work_item,
+                                           db, local_abspath,
+                                           original_checksum,
+                                           cancel_func, cancel_baton,
+                                           result_pool, scratch_pool));
+      cleanup_queue = svn_wc__wq_merge(cleanup_queue, work_item, result_pool);
+    }
 
   /* Merge the changes from the old textbase to the new
      textbase into the file we're updating.
@@ -4134,16 +4175,9 @@ svn_wc__perform_file_merge(svn_skel_t **
                                  result_pool, scratch_pool));
 
   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
-  *found_conflict = (merge_outcome == svn_wc_merge_conflict);
+  *work_items = svn_wc__wq_merge(*work_items, cleanup_queue, result_pool);
 
-  /* If we created a temporary left merge file, get rid of it. */
-  if (delete_left)
-    {
-      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
-                                           merge_left,
-                                           result_pool, scratch_pool));
-      *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
-    }
+  *found_conflict = (merge_outcome == svn_wc_merge_conflict);
 
   return SVN_NO_ERROR;
 }
@@ -4728,9 +4762,9 @@ close_file(void *file_baton,
         }
       else
         {
-          SVN_ERR(svn_wc__db_pristine_read(&src_stream, NULL, eb->db,
-                                           eb->wcroot_abspath, new_checksum,
-                                           scratch_pool, scratch_pool));
+          SVN_ERR(svn_wc__textbase_get_contents(&src_stream, eb->db,
+                                                fb->local_abspath, new_checksum,
+                                                FALSE, scratch_pool, scratch_pool));
         }
 
       SVN_ERR(svn_stream_copy3(src_stream, dst_stream, NULL, NULL, scratch_pool));
@@ -4870,6 +4904,7 @@ update_keywords_after_switch_cb(void *ba
   svn_boolean_t record_fileinfo;
   svn_skel_t *work_items;
   const char *install_from;
+  svn_skel_t *cleanup_work_item;
 
   propval = svn_hash_gets(props, SVN_PROP_KEYWORDS);
   if (!propval)
@@ -4895,11 +4930,18 @@ update_keywords_after_switch_cb(void *ba
       SVN_ERR(svn_stream_copy3(working_stream, install_from_stream,
                                eb->cancel_func, eb->cancel_baton,
                                scratch_pool));
+      SVN_ERR(svn_wc__wq_build_file_remove(&cleanup_work_item, eb->db,
+                                           local_abspath, install_from,
+                                           scratch_pool, scratch_pool));
       record_fileinfo = FALSE;
     }
   else
     {
-      install_from = NULL;
+      SVN_ERR(svn_wc__textbase_setaside_wq(&install_from,
+                                           &cleanup_work_item,
+                                           eb->db, local_abspath, NULL,
+                                           eb->cancel_func, eb->cancel_baton,
+                                           scratch_pool, scratch_pool));
       record_fileinfo = TRUE;
     }
 
@@ -4908,15 +4950,7 @@ update_keywords_after_switch_cb(void *ba
                                         eb->use_commit_times,
                                         record_fileinfo,
                                         scratch_pool, scratch_pool));
-  if (install_from)
-    {
-      svn_skel_t *work_item;
-
-      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
-                                           local_abspath, install_from,
-                                           scratch_pool, scratch_pool));
-      work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
-    }
+  work_items = svn_wc__wq_merge(work_items, cleanup_work_item, scratch_pool);
 
   SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items,
                             scratch_pool));
@@ -5605,33 +5639,39 @@ svn_wc_add_repos_file4(svn_wc_context_t
                                    entry_props, pool, pool));
   }
 
+  {
+    const char *tmp_dir_abspath;
+
+    SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath,
+                                           db, dir_abspath,
+                                           scratch_pool, scratch_pool));
+
+    SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath,
+                                   tmp_dir_abspath, svn_io_file_del_none,
+                                   scratch_pool, scratch_pool));
+  }
+
   /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
      it, and set TMP_TEXT_BASE_ABSPATH to its path.  Compute its
      NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
   if (copyfrom_url)
     {
-      SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents,
-                                                  &install_data,
-                                                  &new_text_base_sha1_checksum,
-                                                  &new_text_base_md5_checksum,
-                                                  wc_ctx->db, local_abspath,
-                                                  scratch_pool, scratch_pool));
+      svn_stream_t *install_stream;
+
+      SVN_ERR(svn_wc__textbase_prepare_install(&install_stream,
+                                               &install_data,
+                                               &new_text_base_sha1_checksum,
+                                               &new_text_base_md5_checksum,
+                                               wc_ctx->db, local_abspath,
+                                               copyfrom_url != NULL,
+                                               scratch_pool, scratch_pool));
+
+      tmp_base_contents = svn_stream_tee(install_stream,
+                                         tmp_base_contents,
+                                         scratch_pool);
     }
   else
     {
-      const char *tmp_dir_abspath;
-
-      /* We are not installing a PRISTINE file, but we use the same code to
-         create whatever we want to install */
-
-      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath,
-                                             db, dir_abspath,
-                                             scratch_pool, scratch_pool));
-
-      SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath,
-                                     tmp_dir_abspath, svn_io_file_del_none,
-                                     scratch_pool, scratch_pool));
-
       new_text_base_sha1_checksum = NULL;
       new_text_base_md5_checksum = NULL;
     }
@@ -5654,6 +5694,10 @@ svn_wc_add_repos_file4(svn_wc_context_t
       SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
                                cancel_func, cancel_baton, pool));
     }
+  else
+    {
+      source_abspath = tmp_text_base_abspath;
+    }
 
   /* Install new text base for copied files. Added files do NOT have a
      text base.  */
@@ -5689,42 +5733,20 @@ svn_wc_add_repos_file4(svn_wc_context_t
       new_text_base_md5_checksum = NULL;
     }
 
-  /* For added files without NEW_CONTENTS, then generate the working file
-     from the provided "pristine" contents.  */
-  if (new_contents == NULL && copyfrom_url == NULL)
-    source_abspath = tmp_text_base_abspath;
-
-  {
-    svn_boolean_t record_fileinfo;
+  /* Install the working copy file (with appropriate translation) from
+     the provided temporary file at SOURCE_ABSPATH.  */
+  SVN_ERR(svn_wc__wq_build_file_install(&work_item,
+                                        db, local_abspath,
+                                        source_abspath,
+                                        FALSE /* use_commit_times */,
+                                        TRUE  /* record_fileinfo */,
+                                        pool, pool));
+  all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
 
-    /* If new contents were provided, then we do NOT want to record the
-       file information. We assume the new contents do not match the
-       "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
-    record_fileinfo = (new_contents == NULL);
-
-    /* Install the working copy file (with appropriate translation) from
-       the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
-       installation from the pristine (available for copied/moved files),
-       or it will specify a temporary file where we placed a "pristine"
-       (for an added file) or a detranslated local-mods file.  */
-    SVN_ERR(svn_wc__wq_build_file_install(&work_item,
-                                          db, local_abspath,
-                                          source_abspath,
-                                          FALSE /* use_commit_times */,
-                                          record_fileinfo,
-                                          pool, pool));
-    all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
-
-    /* If we installed from somewhere besides the official pristine, then
-       it is a temporary file, which needs to be removed.  */
-    if (source_abspath != NULL)
-      {
-        SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
-                                             source_abspath,
-                                             pool, pool));
-        all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
-      }
-  }
+  SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
+                                       source_abspath,
+                                       pool, pool));
+  all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
 
   SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
                                   new_base_props,

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/upgrade.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/upgrade.c?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/upgrade.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/upgrade.c Thu Feb 10 20:04:43 2022
@@ -1078,6 +1078,7 @@ migrate_text_bases(apr_hash_t **text_bas
         SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool));
         SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool));
         SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
+        SVN_ERR(svn_sqlite__bind_int(stmt, 4, TRUE));
         SVN_ERR(svn_sqlite__insert(NULL, stmt));
 
         SVN_ERR(svn_wc__db_pristine_get_future_path(&pristine_path,

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/util.c?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/util.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/util.c Thu Feb 10 20:04:43 2022
@@ -37,6 +37,7 @@
 
 #include "wc.h"   /* just for prototypes of things in this .c file */
 #include "entries.h"
+#include "textbase.h"
 #include "private/svn_wc_private.h"
 
 #include "svn_private_config.h"
@@ -505,8 +506,9 @@ svn_wc__fetch_base_func(const char **fil
       return SVN_NO_ERROR;
     }
 
-  SVN_ERR(svn_wc__db_pristine_get_path(filename, sfb->db, local_abspath,
-                                       checksum, scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc__textbase_setaside(filename, sfb->db, local_abspath,
+                                    checksum, NULL, NULL,
+                                    result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-metadata.sql Thu Feb 10 20:04:43 2022
@@ -570,6 +570,7 @@ CREATE UNIQUE INDEX I_EXTERNALS_DEFINED
                                                       def_local_relpath,
                                                       local_relpath);
 
+/* ------------------------------------------------------------------------- */
 
 /* Identify the WC format corresponding to the schema we have created. */
 PRAGMA user_version =
@@ -706,12 +707,84 @@ WHERE l.op_depth = 0
   AND ((l.repos_id IS NOT r.repos_id)
        OR (l.repos_path IS NOT RELPATH_SKIP_JOIN(r.local_relpath, r.repos_path, l.local_relpath)))
 
-
 /* ------------------------------------------------------------------------- */
-/* Format 32 ....  */
+
+/* Format 32 adds support for optional text-base contents with the
+   following schema changes:
+   - Add the 'hydrated' column to the PRISTINE table.
+   - Add the I_PRISTINE_UNREFERENCED index.
+   - Add the TEXTBASE_REFS 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;
+
+  /* Note: we use checksums to detect if the file contents have been modified
+     in textbase.c and in the svn_wc__internal_file_modified_p() function.
+
+     The new working copy format SHOULD incorporate a switch to a different
+     checksum type without known collisions.
+
+     For the updated pristine table schema, we MAY want to add a new column
+     containing a checksum of the first 8KB of the file to allow saying that
+     the file is modified without reading all its content.  That could speed
+     up the check for large modified files whose size did not change, for
+     example if they are allocated in certain extents.
+   */
+
+CREATE INDEX I_PRISTINE_UNREFERENCED ON PRISTINE (refcount, refcount=0);
+
+/* This table contains references to the on disk text-base contents.
+   Every row corresponds to a row in NODES table with the same key.
+   While a row is present is this table, the contents identified by the
+   corresponding NODES.checksum cannot be dehydrated from the pristine store.
+ */
+CREATE TABLE TEXTBASE_REFS (
+  /* Same key columns as in the NODES table */
+  wc_id  INTEGER NOT NULL,
+  local_relpath  TEXT NOT NULL,
+  op_depth  INTEGER NOT NULL,
+
+  PRIMARY KEY (wc_id, local_relpath, op_depth)
+  );
+
+DROP TRIGGER nodes_delete_trigger;
+
+CREATE TRIGGER nodes_delete_trigger
+AFTER DELETE ON nodes
+WHEN OLD.checksum IS NOT NULL
+BEGIN
+  UPDATE pristine SET refcount = refcount - 1
+  WHERE checksum = OLD.checksum;
+  DELETE FROM textbase_refs
+  WHERE wc_id = OLD.wc_id
+    AND local_relpath = OLD.local_relpath
+    AND op_depth = OLD.op_depth;
+END;
+
+DROP TRIGGER nodes_update_checksum_trigger;
+
+CREATE TRIGGER nodes_update_checksum_trigger
+AFTER UPDATE OF checksum ON nodes
+WHEN NEW.checksum IS NOT OLD.checksum
+  /* AND (NEW.checksum IS NOT NULL OR OLD.checksum IS NOT NULL) */
+BEGIN
+  UPDATE pristine SET refcount = refcount + 1
+  WHERE checksum = NEW.checksum;
+  UPDATE pristine SET refcount = refcount - 1
+  WHERE checksum = OLD.checksum;
+  DELETE FROM textbase_refs
+  WHERE wc_id = OLD.wc_id
+    AND local_relpath = OLD.local_relpath
+    AND op_depth = OLD.op_depth;
+END;
+
 PRAGMA user_version = 32;
 
+/* ------------------------------------------------------------------------- */
+/* Format 33 ....  */
+/* -- STMT_UPGRADE_TO_33
+PRAGMA user_version = 33; */
+
 
 /* ------------------------------------------------------------------------- */
 

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-queries.sql?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc-queries.sql Thu Feb 10 20:04:43 2022
@@ -884,23 +884,22 @@ SELECT id, work FROM work_queue ORDER BY
 DELETE FROM work_queue WHERE id = ?1
 
 -- STMT_INSERT_OR_IGNORE_PRISTINE
-INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount)
-VALUES (?1, ?2, ?3, 0)
+INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount, hydrated)
+VALUES (?1, ?2, ?3, 0, ?4)
 
--- STMT_INSERT_PRISTINE
-INSERT INTO pristine (checksum, md5_checksum, size, refcount)
-VALUES (?1, ?2, ?3, 0)
+-- STMT_UPSERT_PRISTINE
+/* ### Probably need to bump the minimum SQLite version for UPSERT support
+   https://www.sqlite.org/lang_UPSERT.html
+ */
+INSERT INTO pristine (checksum, md5_checksum, size, refcount, hydrated)
+VALUES (?1, ?2, ?3, 0, ?4)
+ON CONFLICT(checksum) DO UPDATE SET size=?3, hydrated=?4
 
 -- STMT_SELECT_PRISTINE
-SELECT md5_checksum
+SELECT md5_checksum, size, hydrated
 FROM pristine
 WHERE checksum = ?1
 
--- STMT_SELECT_PRISTINE_SIZE
-SELECT size
-FROM pristine
-WHERE checksum = ?1 LIMIT 1
-
 -- STMT_SELECT_PRISTINE_BY_MD5
 SELECT checksum
 FROM pristine
@@ -917,7 +916,7 @@ WHERE checksum = ?1 AND refcount = 0
 
 -- STMT_SELECT_COPY_PRISTINES
 /* For the root itself */
-SELECT n.checksum, md5_checksum, size
+SELECT n.checksum, md5_checksum, size, p.hydrated
 FROM nodes_current n
 LEFT JOIN pristine p ON n.checksum = p.checksum
 WHERE wc_id = ?1
@@ -925,7 +924,7 @@ WHERE wc_id = ?1
   AND n.checksum IS NOT NULL
 UNION ALL
 /* And all descendants */
-SELECT n.checksum, md5_checksum, size
+SELECT n.checksum, md5_checksum, size, p.hydrated
 FROM nodes n
 LEFT JOIN pristine p ON n.checksum = p.checksum
 WHERE wc_id = ?1
@@ -934,6 +933,10 @@ WHERE wc_id = ?1
       (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2)
   AND n.checksum IS NOT NULL
 
+-- STMT_UPDATE_PRISTINE_HYDRATED
+UPDATE pristine SET hydrated = ?2
+WHERE checksum = ?1
+
 -- STMT_VACUUM
 VACUUM
 
@@ -1812,6 +1815,53 @@ SELECT lock_token, lock_owner, lock_comm
 FROM lock
 WHERE repos_id = ?1 AND (repos_relpath = ?2)
 
+-- STMT_TEXTBASE_ADD_REF
+INSERT OR IGNORE INTO textbase_refs (wc_id, local_relpath, op_depth)
+VALUES (?1, ?2, ?3)
+
+-- STMT_TEXTBASE_REMOVE_REF
+DELETE FROM textbase_refs
+WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
+
+-- STMT_TEXTBASE_WALK
+SELECT refs.wc_id IS NOT NULL,
+       nodes.local_relpath, nodes.op_depth,
+       nodes.checksum, nodes.properties,
+       nodes.translated_size, nodes.last_mod_time,
+       (SELECT properties FROM ACTUAL_NODE a
+        WHERE a.wc_id = ?1
+          AND a.local_relpath = nodes.local_relpath
+        LIMIT 1),
+       (SELECT MAX(op_depth)
+          FROM NODES w
+         WHERE w.wc_id = ?1
+           AND w.local_relpath = nodes.local_relpath)
+FROM nodes
+LEFT OUTER JOIN textbase_refs refs ON nodes.wc_id = refs.wc_id
+                                  AND nodes.local_relpath = refs.local_relpath
+                                  AND nodes.op_depth = refs.op_depth
+WHERE nodes.wc_id = ?1
+  AND (nodes.local_relpath = ?2
+       OR IS_STRICT_DESCENDANT_OF(nodes.local_relpath, ?2))
+  AND nodes.checksum IS NOT NULL
+
+-- STMT_TEXTBASE_SYNC
+SELECT pristine.checksum,
+       MIN(pristine.hydrated != 0),
+       MAX(refs.wc_id IS NOT NULL),
+       nodes.repos_path, nodes.repos_id, nodes.revision
+FROM nodes
+JOIN pristine ON nodes.wc_id = ?1
+             AND nodes.checksum = pristine.checksum
+             AND (nodes.local_relpath = ?2
+                  OR IS_STRICT_DESCENDANT_OF(nodes.local_relpath, ?2))
+LEFT OUTER JOIN textbase_refs refs ON nodes.wc_id = refs.wc_id
+                                  AND nodes.local_relpath = refs.local_relpath
+                                  AND nodes.op_depth = refs.op_depth
+GROUP BY pristine.checksum
+UNION ALL
+SELECT pristine.checksum, pristine.hydrated, 0, NULL, NULL, NULL
+FROM pristine WHERE refcount = 0
 
 /* ------------------------------------------------------------------------- */
 

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc_db.h?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc_db.h Thu Feb 10 20:04:43 2022
@@ -927,21 +927,6 @@ svn_wc__db_base_get_lock_tokens_recursiv
    @{
 */
 
-/* Set *PRISTINE_ABSPATH to the path to the pristine text file
-   identified by SHA1_CHECKSUM.  Error if it does not exist.
-
-   ### This is temporary - callers should not be looking at the file
-   directly.
-
-   Allocate the path in RESULT_POOL. */
-svn_error_t *
-svn_wc__db_pristine_get_path(const char **pristine_abspath,
-                             svn_wc__db_t *db,
-                             const char *wri_abspath,
-                             const svn_checksum_t *checksum,
-                             apr_pool_t *result_pool,
-                             apr_pool_t *scratch_pool);
-
 /* Set *PRISTINE_ABSPATH to the path under WCROOT_ABSPATH that will be
    used by the pristine text identified by SHA1_CHECKSUM.  The file
    need not exist.
@@ -986,6 +971,8 @@ typedef struct svn_wc__db_install_data_t
    set to the MD-5 and SHA-1 checksums respectively of that file.
    MD5_CHECKSUM and/or SHA1_CHECKSUM may be NULL if not wanted.
 
+   If HYDRATED is true, the contents of the pristine will be saved to disk.
+
    Allocate the new stream, path and checksums in RESULT_POOL.
  */
 svn_error_t *
@@ -995,6 +982,7 @@ svn_wc__db_pristine_prepare_install(svn_
                                     svn_checksum_t **md5_checksum,
                                     svn_wc__db_t *db,
                                     const char *wri_abspath,
+                                    svn_boolean_t hydrated,
                                     apr_pool_t *result_pool,
                                     apr_pool_t *scratch_pool);
 
@@ -1077,14 +1065,26 @@ svn_wc__db_pristine_cleanup(svn_wc__db_t
 
 /* Set *PRESENT to true if the pristine store for WRI_ABSPATH in DB contains
    a pristine text with SHA-1 checksum SHA1_CHECKSUM, and to false otherwise.
-*/
+   If the pristine is present, set *HYDRATED to true if its contents are
+   currently available on disk, and to false otherwise.  If the pristine
+   is not present, set *HYDRATED to false. */
 svn_error_t *
 svn_wc__db_pristine_check(svn_boolean_t *present,
+                          svn_boolean_t *hydrated,
                           svn_wc__db_t *db,
                           const char *wri_abspath,
                           const svn_checksum_t *sha1_checksum,
                           apr_pool_t *scratch_pool);
 
+/* If the pristine store for WRI_ABSPATH in DB contains a pristine text with
+   SHA-1 checksum SHA1_CHECKSUM with its content available on disk, remove
+   that content and mark the pristine entry as "dehydrated". */
+svn_error_t *
+svn_wc__db_pristine_dehydrate(svn_wc__db_t *db,
+                              const char *wri_abspath,
+                              const svn_checksum_t *sha1_checksum,
+                              apr_pool_t *scratch_pool);
+
 /* @defgroup svn_wc__db_external  External management
    @{ */
 
@@ -3121,6 +3121,77 @@ svn_wc__db_wclock_owns_lock(svn_boolean_
                             svn_boolean_t exact,
                             apr_pool_t *scratch_pool);
 
+/* @} */
+
+
+/* @defgroup svn_wc__db_textbase  Working with text-bases
+   @{
+*/
+
+/* The callback invoked by svn_wc__db_textbase_walk(). */
+typedef svn_error_t * (*svn_wc__db_textbase_walk_cb_t)(
+  svn_boolean_t *referenced_p,
+  void *baton,
+  const char *local_abspath,
+  int op_depth,
+  const svn_checksum_t *checksum,
+  svn_boolean_t have_props,
+  svn_boolean_t props_mod,
+  svn_filesize_t recorded_size,
+  apr_time_t recorded_time,
+  int max_op_depth,
+  apr_pool_t *scratch_pool);
+
+/* Walk the text-bases referenced by the nodes in the LOCAL_ABSPATH
+   tree, invoking the CALLBACK with information about each text-base
+   for a node.
+
+   If the callback sets *REFERENCED_P to true, ensure that a text-base
+   reference exists for the node.  If the callback sets *REFERENCED_P to
+   false, ensure that a text-base reference does not exist for the node.
+
+   See the description of the `TEXTBASE_REFS` table in the schema.
+ */
+svn_error_t *
+svn_wc__db_textbase_walk(svn_wc__db_t *db,
+                         const char *local_abspath,
+                         svn_wc__db_textbase_walk_cb_t callback,
+                         void *callback_baton,
+                         svn_cancel_func_t cancel_func,
+                         void *cancel_baton,
+                         apr_pool_t *scratch_pool);
+
+/* The callback invoked by svn_wc__db_textbase_sync(). */
+typedef svn_error_t * (*svn_wc__db_textbase_hydrate_cb_t)(
+  void *baton,
+  const char *repos_root_url,
+  const char *repos_relpath,
+  svn_revnum_t revision,
+  svn_stream_t *contents,
+  svn_cancel_func_t cancel_func,
+  void *cancel_baton,
+  apr_pool_t *scratch_pool);
+
+/* Synchronize the state of the text-bases in DB.
+
+   If ALLOW_HYDRATE is true, fetch the referenced but missing text-base
+   contents using the provided HYDRATE_CALLBACK and HYDRATE_BATON.
+   If ALLOW_DEHYDRATE is true, remove the on disk text-base contents
+   that is no longer referenced.
+ */
+svn_error_t *
+svn_wc__db_textbase_sync(svn_wc__db_t *db,
+                         const char *local_abspath,
+                         svn_boolean_t allow_hydrate,
+                         svn_boolean_t allow_dehydrate,
+                         svn_wc__db_textbase_hydrate_cb_t hydrate_callback,
+                         void *hydrate_baton,
+                         svn_cancel_func_t cancel_func,
+                         void *cancel_baton,
+                         apr_pool_t *scratch_pool);
+
+
+/* @} */
 
 
 /* @defgroup svn_wc__db_temp Various temporary functions during transition

Modified: subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc_db_pristine.c
URL: http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc_db_pristine.c?rev=1897946&r1=1897945&r2=1897946&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc_db_pristine.c (original)
+++ subversion/branches/pristines-on-demand-on-mwf/subversion/libsvn_wc/wc_db_pristine.c Thu Feb 10 20:04:43 2022
@@ -94,50 +94,6 @@ get_pristine_fname(const char **pristine
 
 
 svn_error_t *
-svn_wc__db_pristine_get_path(const char **pristine_abspath,
-                             svn_wc__db_t *db,
-                             const char *wri_abspath,
-                             const svn_checksum_t *sha1_checksum,
-                             apr_pool_t *result_pool,
-                             apr_pool_t *scratch_pool)
-{
-  svn_wc__db_wcroot_t *wcroot;
-  const char *local_relpath;
-  svn_boolean_t present;
-
-  SVN_ERR_ASSERT(pristine_abspath != NULL);
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
-  SVN_ERR_ASSERT(sha1_checksum != NULL);
-  /* ### Transitional: accept MD-5 and look up the SHA-1.  Return an error
-   * if the pristine text is not in the store. */
-  if (sha1_checksum->kind != svn_checksum_sha1)
-    SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath,
-                                         sha1_checksum,
-                                         scratch_pool, scratch_pool));
-  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
-
-  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
-                                             db, wri_abspath,
-                                             scratch_pool, scratch_pool));
-  VERIFY_USABLE_WCROOT(wcroot);
-
-  SVN_ERR(svn_wc__db_pristine_check(&present, db, wri_abspath, sha1_checksum,
-                                    scratch_pool));
-  if (! present)
-    return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
-                             _("The pristine text with checksum '%s' was "
-                               "not found"),
-                             svn_checksum_to_cstring_display(sha1_checksum,
-                                                             scratch_pool));
-
-  SVN_ERR(get_pristine_fname(pristine_abspath, wcroot->abspath,
-                             sha1_checksum,
-                             result_pool, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
 svn_wc__db_pristine_get_future_path(const char **pristine_abspath,
                                     const char *wcroot_abspath,
                                     const svn_checksum_t *sha1_checksum,
@@ -152,9 +108,10 @@ svn_wc__db_pristine_get_future_path(cons
 
 /* Set *CONTENTS to a readable stream from which the pristine text
  * identified by SHA1_CHECKSUM and PRISTINE_ABSPATH can be read from the
- * pristine store of WCROOT.  If SIZE is not null, set *SIZE to the size
- * in bytes of that text. If that text is not in the pristine store,
- * return an error.
+ * pristine store of WCROOT.  If the pristine contents are currently not
+ * available on disk, set *CONTENTS to NULL.  If SIZE is not null, set
+ * *SIZE to the size in bytes of that text.  If that text is not in
+ * the pristine store, return an error.
  *
  * Even if the pristine text is removed from the store while it is being
  * read, the stream will remain valid and readable until it is closed.
@@ -176,16 +133,18 @@ pristine_read_txn(svn_stream_t **content
 {
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
+  svn_boolean_t hydrated;
 
   /* Check that this pristine text is present in the store.  (The presence
    * of the file is not sufficient.) */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_PRISTINE_SIZE));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
   SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
 
   if (size)
-    *size = svn_sqlite__column_int64(stmt, 0);
+    *size = svn_sqlite__column_int64(stmt, 1);
+
+  hydrated = svn_sqlite__column_boolean(stmt, 2);
 
   SVN_ERR(svn_sqlite__reset(stmt));
   if (! have_row)
@@ -196,19 +155,27 @@ pristine_read_txn(svn_stream_t **content
                                  sha1_checksum, scratch_pool));
     }
 
-  /* Open the file as a readable stream.  It will remain readable even when
-   * deleted from disk; APR guarantees that on Windows as well as Unix.
-   *
-   * We also don't enable APR_BUFFERED on this file to maximize throughput
-   * e.g. for fulltext comparison.  As we use SVN__STREAM_CHUNK_SIZE buffers
-   * where needed in streams, there is no point in having another layer of
-   * buffers. */
   if (contents)
     {
-      apr_file_t *file;
-      SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ,
-                               APR_OS_DEFAULT, result_pool));
-      *contents = svn_stream_from_aprfile2(file, FALSE, result_pool);
+      if (hydrated)
+        {
+          /* Open the file as a readable stream.  It will remain readable even when
+           * deleted from disk; APR guarantees that on Windows as well as Unix.
+           *
+           * We also don't enable APR_BUFFERED on this file to maximize throughput
+           * e.g. for fulltext comparison.  As we use SVN__STREAM_CHUNK_SIZE buffers
+           * where needed in streams, there is no point in having another layer of
+           * buffers. */
+
+          apr_file_t *file;
+          SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ,
+                                   APR_OS_DEFAULT, result_pool));
+          *contents = svn_stream_from_aprfile2(file, FALSE, result_pool);
+        }
+      else
+        {
+          *contents = NULL;
+        }
     }
 
   return SVN_NO_ERROR;
@@ -256,6 +223,53 @@ svn_wc__db_pristine_read(svn_stream_t **
 }
 
 
+struct svn_wc__db_install_data_t
+{
+  svn_wc__db_wcroot_t *wcroot;
+  svn_stream_t *inner_stream;
+  apr_off_t size;
+};
+
+static svn_error_t *
+install_stream_write_fn(void *baton, const char *data, apr_size_t *len)
+{
+  svn_wc__db_install_data_t *install_data = baton;
+
+  if (install_data->inner_stream)
+    SVN_ERR(svn_stream_write(install_data->inner_stream, data, len));
+
+  install_data->size += *len;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+install_stream_seek_fn(void *baton, const svn_stream_mark_t *mark)
+{
+  svn_wc__db_install_data_t *install_data = baton;
+
+  if (!mark)
+    return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL);
+
+  if (install_data->inner_stream)
+    SVN_ERR(svn_stream_reset(install_data->inner_stream));
+
+  install_data->size = 0;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+install_stream_close_fn(void *baton)
+{
+  svn_wc__db_install_data_t *install_data = baton;
+
+  if (install_data->inner_stream)
+    SVN_ERR(svn_stream_close(install_data->inner_stream));
+
+  return SVN_NO_ERROR;
+}
+
 /* Return the absolute path to the temporary directory for pristine text
    files within WCROOT. */
 static char *
@@ -268,9 +282,8 @@ pristine_get_tempdir(svn_wc__db_wcroot_t
                               PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL);
 }
 
-/* Install the pristine text described by BATON into the pristine store of
- * SDB.  If it is already stored then just delete the new file
- * BATON->tempfile_abspath.
+/* Install the pristine text described by INSTALL_DATA into the pristine store
+ * of SDB.
  *
  * This function expects to be executed inside a SQLite txn that has already
  * acquired a 'RESERVED' lock.
@@ -279,8 +292,7 @@ pristine_get_tempdir(svn_wc__db_wcroot_t
  */
 static svn_error_t *
 pristine_install_txn(svn_sqlite__db_t *sdb,
-                     /* The path to the source file that is to be moved into place. */
-                     svn_stream_t *install_stream,
+                     svn_wc__db_install_data_t *install_data,
                      /* The target path for the file (within the pristine store). */
                      const char *pristine_abspath,
                      /* The pristine text's SHA-1 checksum. */
@@ -289,85 +301,73 @@ pristine_install_txn(svn_sqlite__db_t *s
                      const svn_checksum_t *md5_checksum,
                      apr_pool_t *scratch_pool)
 {
+  svn_stream_t *install_stream = install_data->inner_stream;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
+  svn_boolean_t hydrated;
 
-  /* If this pristine text is already present in the store, just keep it:
-   * delete the new one and return. */
   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_PRISTINE));
   SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  SVN_ERR(svn_sqlite__reset(stmt));
 
   if (have_row)
+    hydrated = svn_sqlite__column_boolean(stmt, 2);
+  else
+    hydrated = FALSE;
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (have_row && hydrated)
     {
-#ifdef SVN_DEBUG
-      /* Consistency checks.  Verify both files exist and match.
-       * ### We could check much more. */
-      {
-        apr_finfo_t finfo;
-        apr_off_t size;
-
-        SVN_ERR(svn_stream__install_finalize(NULL, &size, install_stream,
-                                             scratch_pool));
-
-        SVN_ERR(svn_io_stat(&finfo, pristine_abspath, APR_FINFO_SIZE,
-                            scratch_pool));
-        if (size != finfo.size)
-          {
-            return svn_error_createf(
-              SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
-              _("New pristine text '%s' has different size: %s versus %s"),
-              svn_checksum_to_cstring_display(sha1_checksum, scratch_pool),
-              apr_off_t_toa(scratch_pool, size),
-              apr_off_t_toa(scratch_pool, finfo.size));
-          }
-      }
-#endif
+      /* For now, ensure that we do not inadvertently dehydrate an existing
+       * hydrated entry, as there could be references to its content. */
+
+      if (install_stream)
+        SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool));
 
-      /* Remove the temp file: it's already there */
-      SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool));
       return SVN_NO_ERROR;
     }
 
-  /* Move the file to its target location.  (If it is already there, it is
-   * an orphan file and it doesn't matter if we overwrite it.) */
-  {
-    apr_off_t size;
+  if (install_stream)
+    {
+      /* Move the file to its target location.  (If it is already there, it is
+       * an orphan file and it doesn't matter if we overwrite it.) */
 
-    svn_stream__install_set_read_only(install_stream, TRUE);
+      svn_stream__install_set_read_only(install_stream, TRUE);
 
-    SVN_ERR(svn_stream__install_finalize(NULL, &size, install_stream,
-                                         scratch_pool));
-    SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath,
-                                       TRUE, scratch_pool));
-
-    SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE));
-    SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
-    SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
-    SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size));
-    SVN_ERR(svn_sqlite__insert(NULL, stmt));
-  }
+      SVN_ERR(svn_stream__install_finalize(NULL, NULL, install_stream,
+                                           scratch_pool));
+      SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath,
+                                         TRUE, scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool));
+    }
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_UPSERT_PRISTINE));
+  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
+  SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
+  SVN_ERR(svn_sqlite__bind_int64(stmt, 3, install_data->size));
+  SVN_ERR(svn_sqlite__bind_int(stmt, 4, install_stream != NULL));
+  SVN_ERR(svn_sqlite__insert(NULL, stmt));
 
   return SVN_NO_ERROR;
 }
 
-struct svn_wc__db_install_data_t
-{
-  svn_wc__db_wcroot_t *wcroot;
-  svn_stream_t *inner_stream;
-};
-
 svn_error_t *
-svn_wc__db_pristine_prepare_install(svn_stream_t **stream,
-                                    svn_wc__db_install_data_t **install_data,
-                                    svn_checksum_t **sha1_checksum,
-                                    svn_checksum_t **md5_checksum,
+svn_wc__db_pristine_prepare_install(svn_stream_t **stream_p,
+                                    svn_wc__db_install_data_t **install_data_p,
+                                    svn_checksum_t **sha1_checksum_p,
+                                    svn_checksum_t **md5_checksum_p,
                                     svn_wc__db_t *db,
                                     const char *wri_abspath,
+                                    svn_boolean_t hydrated,
                                     apr_pool_t *result_pool,
                                     apr_pool_t *scratch_pool)
 {
+  svn_stream_t *stream;
+  svn_wc__db_install_data_t *install_data;
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
   const char *temp_dir_abspath;
@@ -380,22 +380,37 @@ svn_wc__db_pristine_prepare_install(svn_
 
   temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool);
 
-  *install_data = apr_pcalloc(result_pool, sizeof(**install_data));
-  (*install_data)->wcroot = wcroot;
+  install_data = apr_pcalloc(result_pool, sizeof(*install_data));
+  install_data->wcroot = wcroot;
+
+  if (hydrated)
+    {
+      SVN_ERR_W(svn_stream__create_for_install(&install_data->inner_stream,
+                                               temp_dir_abspath,
+                                               result_pool, scratch_pool),
+                _("Unable to create pristine install stream"));
+    }
+  else
+    {
+      install_data->inner_stream = NULL;
+    }
+
+  install_data->size = 0;
+
+  stream = svn_stream_create(install_data, result_pool);
+  svn_stream_set_write(stream, install_stream_write_fn);
+  svn_stream_set_seek(stream, install_stream_seek_fn);
+  svn_stream_set_close(stream, install_stream_close_fn);
+
+  if (md5_checksum_p)
+    stream = svn_stream_checksummed2(stream, NULL, md5_checksum_p,
+                                     svn_checksum_md5, FALSE, result_pool);
+  if (sha1_checksum_p)
+    stream = svn_stream_checksummed2(stream, NULL, sha1_checksum_p,
+                                     svn_checksum_sha1, FALSE, result_pool);
 
-  SVN_ERR_W(svn_stream__create_for_install(stream,
-                                           temp_dir_abspath,
-                                           result_pool, scratch_pool),
-            _("Unable to create pristine install stream"));
-
-  (*install_data)->inner_stream = *stream;
-
-  if (md5_checksum)
-    *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum,
-                                      svn_checksum_md5, FALSE, result_pool);
-  if (sha1_checksum)
-    *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum,
-                                      svn_checksum_sha1, FALSE, result_pool);
+  *stream_p = stream;
+  *install_data_p = install_data;
 
   return SVN_NO_ERROR;
 }
@@ -422,7 +437,7 @@ svn_wc__db_pristine_install(svn_wc__db_i
    * at the disk, to ensure no concurrent pristine install/delete txn. */
   SVN_SQLITE__WITH_IMMEDIATE_TXN(
     pristine_install_txn(wcroot->sdb,
-                         install_data->inner_stream, pristine_abspath,
+                         install_data, pristine_abspath,
                          sha1_checksum, md5_checksum,
                          scratch_pool),
     wcroot->sdb);
@@ -434,8 +449,12 @@ svn_error_t *
 svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data,
                                   apr_pool_t *scratch_pool)
 {
-  return svn_error_trace(svn_stream__install_delete(install_data->inner_stream,
-                                                    scratch_pool));
+  if (install_data->inner_stream)
+    SVN_ERR(svn_stream__install_delete(install_data->inner_stream, scratch_pool));
+
+  install_data->size = 0;
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -516,84 +535,91 @@ svn_wc__db_pristine_get_sha1(const svn_c
 }
 
 /* Handle the moving of a pristine from SRC_WCROOT to DST_WCROOT. The existing
-   pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM and SIZE */
+   pristine in SRC_WCROOT is described by CHECKSUM, MD5_CHECKSUM, SIZE and
+   HYDRATED. */
 static svn_error_t *
 maybe_transfer_one_pristine(svn_wc__db_wcroot_t *src_wcroot,
                             svn_wc__db_wcroot_t *dst_wcroot,
                             const svn_checksum_t *checksum,
                             const svn_checksum_t *md5_checksum,
                             apr_int64_t size,
+                            svn_boolean_t hydrated,
                             svn_cancel_func_t cancel_func,
                             void *cancel_baton,
                             apr_pool_t *scratch_pool)
 {
-  const char *pristine_abspath;
   svn_sqlite__stmt_t *stmt;
-  svn_stream_t *src_stream;
-  svn_stream_t *dst_stream;
-  const char *tmp_abspath;
-  const char *src_abspath;
   int affected_rows;
-  svn_error_t *err;
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
                                     STMT_INSERT_OR_IGNORE_PRISTINE));
   SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, checksum, scratch_pool));
   SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
   SVN_ERR(svn_sqlite__bind_int64(stmt, 3, size));
+  SVN_ERR(svn_sqlite__bind_int(stmt, 4, hydrated));
 
   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 
   if (affected_rows == 0)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath,
-                                 pristine_get_tempdir(dst_wcroot,
-                                                      scratch_pool,
-                                                      scratch_pool),
-                                 svn_io_file_del_on_pool_cleanup,
-                                 scratch_pool, scratch_pool));
+  if (hydrated)
+    {
+      const char *pristine_abspath;
+      svn_stream_t *src_stream;
+      svn_stream_t *dst_stream;
+      const char *tmp_abspath;
+      const char *src_abspath;
+      svn_error_t *err;
 
-  SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum,
-                             scratch_pool, scratch_pool));
+      SVN_ERR(svn_stream_open_unique(&dst_stream, &tmp_abspath,
+                                     pristine_get_tempdir(dst_wcroot,
+                                                          scratch_pool,
+                                                          scratch_pool),
+                                     svn_io_file_del_on_pool_cleanup,
+                                     scratch_pool, scratch_pool));
 
-  SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath,
-                                   scratch_pool, scratch_pool));
+      SVN_ERR(get_pristine_fname(&src_abspath, src_wcroot->abspath, checksum,
+                                 scratch_pool, scratch_pool));
 
-  /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */
-  SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
-                           cancel_func, cancel_baton,
-                           scratch_pool));
+      SVN_ERR(svn_stream_open_readonly(&src_stream, src_abspath,
+                                       scratch_pool, scratch_pool));
 
-  SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum,
-                             scratch_pool, scratch_pool));
+      /* ### Should we verify the SHA1 or MD5 here, or is that too expensive? */
+      SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
+                               cancel_func, cancel_baton,
+                               scratch_pool));
 
-  /* Move the file to its target location.  (If it is already there, it is
-   * an orphan file and it doesn't matter if we overwrite it.) */
-  err = svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
-                            scratch_pool);
-
-  /* Maybe the directory doesn't exist yet? */
-  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
-    {
-      svn_error_t *err2;
-
-      err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath,
-                                                scratch_pool),
-                             APR_OS_DEFAULT, scratch_pool);
-
-      if (err2)
-        /* Creating directory didn't work: Return all errors */
-        return svn_error_trace(svn_error_compose_create(err, err2));
-      else
-        /* We could create a directory: retry install */
-        svn_error_clear(err);
+      SVN_ERR(get_pristine_fname(&pristine_abspath, dst_wcroot->abspath, checksum,
+                                 scratch_pool, scratch_pool));
 
-      SVN_ERR(svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
-                                  scratch_pool));
+      /* Move the file to its target location.  (If it is already there, it is
+       * an orphan file and it doesn't matter if we overwrite it.) */
+      err = svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
+                                scratch_pool);
+
+      /* Maybe the directory doesn't exist yet? */
+      if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+        {
+          svn_error_t *err2;
+
+          err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath,
+                                                    scratch_pool),
+                                 APR_OS_DEFAULT, scratch_pool);
+
+          if (err2)
+            /* Creating directory didn't work: Return all errors */
+            return svn_error_trace(svn_error_compose_create(err, err2));
+          else
+            /* We could create a directory: retry install */
+            svn_error_clear(err);
+
+          SVN_ERR(svn_io_file_rename2(tmp_abspath, pristine_abspath, FALSE,
+                                      scratch_pool));
+        }
+      else
+        SVN_ERR(err);
     }
-  else
-    SVN_ERR(err);
 
   return SVN_NO_ERROR;
 }
@@ -625,6 +651,7 @@ pristine_transfer_txn(svn_wc__db_wcroot_
       const svn_checksum_t *checksum;
       const svn_checksum_t *md5_checksum;
       apr_int64_t size;
+      svn_boolean_t hydrated;
       svn_error_t *err;
 
       svn_pool_clear(iterpool);
@@ -632,9 +659,11 @@ pristine_transfer_txn(svn_wc__db_wcroot_
       SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0, iterpool));
       SVN_ERR(svn_sqlite__column_checksum(&md5_checksum, stmt, 1, iterpool));
       size = svn_sqlite__column_int64(stmt, 2);
+      hydrated = svn_sqlite__column_boolean(stmt, 3);
 
       err = maybe_transfer_one_pristine(src_wcroot, dst_wcroot,
                                         checksum, md5_checksum, size,
+                                        hydrated,
                                         cancel_func, cancel_baton,
                                         iterpool);
 
@@ -714,19 +743,7 @@ pristine_remove_if_unreferenced_txn(svn_
 
   /* If we removed the DB row, then remove the file. */
   if (affected_rows > 0)
-    {
-      /* If the file is not present, something has gone wrong, but at this
-       * point it no longer matters.  In a debug build, raise an error, but
-       * in a release build, it is more helpful to ignore it and continue. */
-#ifdef SVN_DEBUG
-      svn_boolean_t ignore_enoent = FALSE;
-#else
-      svn_boolean_t ignore_enoent = TRUE;
-#endif
-
-      SVN_ERR(svn_io_remove_file2(pristine_abspath, ignore_enoent,
-                                  scratch_pool));
-    }
+    SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -887,6 +904,7 @@ svn_wc__db_pristine_cleanup(svn_wc__db_t
 
 svn_error_t *
 svn_wc__db_pristine_check(svn_boolean_t *present,
+                          svn_boolean_t *hydrated,
                           svn_wc__db_t *db,
                           const char *wri_abspath,
                           const svn_checksum_t *sha1_checksum,
@@ -903,6 +921,9 @@ svn_wc__db_pristine_check(svn_boolean_t
   if (sha1_checksum->kind != svn_checksum_sha1)
     {
       *present = FALSE;
+      if (hydrated)
+        *hydrated = FALSE;
+
       return SVN_NO_ERROR;
     }
 
@@ -910,46 +931,50 @@ svn_wc__db_pristine_check(svn_boolean_t
                               wri_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  /* A filestat is much cheaper than a sqlite transaction especially on NFS,
-     so first check if there is a pristine file and then if we are allowed
-     to use it. */
-  {
-    const char *pristine_abspath;
-    svn_node_kind_t kind_on_disk;
-    svn_error_t *err;
-
-    SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
-                               sha1_checksum, scratch_pool, scratch_pool));
-    err = svn_io_check_path(pristine_abspath, &kind_on_disk, scratch_pool);
-#ifdef WIN32
-    if (err && err->apr_err == APR_FROM_OS_ERROR(ERROR_ACCESS_DENIED))
-      {
-        svn_error_clear(err);
-        /* Possible race condition: The filename is locked, but there is no
-           file or dir with this name. Let's fall back on checking the DB.
-
-           This case is triggered by the pristine store tests on deleting
-           a file that is still open via another handle, where this other
-           handle has a FILE_SHARE_DELETE share mode.
-         */
-      }
-    else
-#endif
-    if (err)
-      return svn_error_trace(err);
-    else if (kind_on_disk != svn_node_file)
-      {
-        *present = FALSE;
-        return SVN_NO_ERROR;
-      }
-  }
-
   /* Check that there is an entry in the PRISTINE table. */
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_PRISTINE));
   SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  if (hydrated)
+    *hydrated = svn_sqlite__column_boolean(stmt, 2);
+
   SVN_ERR(svn_sqlite__reset(stmt));
 
   *present = have_row;
   return SVN_NO_ERROR;
 }
+
+
+svn_error_t *
+svn_wc__db_pristine_dehydrate(svn_wc__db_t *db,
+                              const char *wri_abspath,
+                              const svn_checksum_t *sha1_checksum,
+                              apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+  const char *pristine_abspath;
+  svn_sqlite__stmt_t *stmt;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
+  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                              wri_abspath, scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath,
+                             sha1_checksum,
+                             scratch_pool, scratch_pool));
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_UPDATE_PRISTINE_HYDRATED));
+  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
+  SVN_ERR(svn_sqlite__bind_int(stmt, 2, FALSE));
+  SVN_ERR(svn_sqlite__update(NULL, stmt));
+
+  SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool));
+
+  return SVN_NO_ERROR;
+}