Author: kotkov
Date: Fri Aug 27 13:54:33 2021
New Revision: 1892650

URL: http://svn.apache.org/viewvc?rev=1892650&view=rev
Log:
On the pristines-on-demand branch: First cut at making the pristine text-base
files optional and retrieving their contents on demand for modified files.

The core idea of the approach is:

 1. To avoid having to access the text-base, the "is the file modified?" check
    is performed by calculating the checksum of a file and comparing that to
    what's recorded in the working copy.

 2. A text-base of the unmodified file is the file itself, appropriately
    detranslated.

 3. We start to maintain the following invariant: only the modified files
    have their pristine text-base files available on the disk.

    To get into this state at the beginning of the operation, we walk through
    the current text-base info in the db and check if the corresponding working
    files are modified.  The missing text-bases are fetched using the svn_ra
    layer.  The operations also include a final step during which the no longer
    required text-bases are removed from disk.

    The operations that don't need to access the text-bases (such as "svn ls"
    or the updated "svn st") do not perform this walk and do not synchronize
    the text-base state.

With our previous work on this branch, 1. and 2. are already in place, so
in this changeset we'll try to focus on part 3.

We introduce a new function, svn_wc__textbase_sync() and its counterpart,
svn_client__textbase_sync(), and update the relevant operations on the client
operations so that they would synchronize the text-base state at the beginning
and at the end of the operation.  We call a known text-base "hydrated" if
it has its contents available on disk and "dehydrated" otherwise.  At the
beginning of the operation, sync is allowed to both hydrate and dehydrate the
text-bases.  At the end of the operation, sync is only allowed to dehydrate.

This is an incompatible change, so we bump the working copy format.
Although this would most certainly have to change in the future, for now
any working copy of the new format is considered to always have optional
text-bases (for easier testing, etc.).

Within the test suite, I flagged the tests that would need to have their
expectations updated.  For example, we have tests that directly access the
pristine file with svntest.wc.text_base_path() and expect it to exist.
I also added notes on a couple of existing test issues that will most likely
need to be fixed separately, for example in the auto_analyze() test.

* subversion/libsvn_client/client.h
  (svn_client__textbase_sync): Declare.

* subversion/libsvn_client/textbase.c: New file.
  (struct textbase_hydrate_baton_t): Declare.
  (textbase_hydrate_cb): Provide the initial implementation of this callback
   that fetches the text-base contents.  Note on the suboptimal session usage.
  (svn_client__textbase_sync): New function, calls svn_wc__textbase_sync()
   internally.

* subversion/libsvn_client/cat.c
  (svn_client_cat3): Call svn_client__textbase_sync().

* subversion/libsvn_client/commit.c
  (svn_client_commit6): Call svn_client__textbase_sync().

* subversion/libsvn_client/conflicts.c
  (begin_resolve, finish_resolve): New helper functions. Acquires the lock
   and calls svn_client__textbase_sync().
  (resolve_text_conflict,
   resolve_prop_conflict,
   resolve_accept_current_wc_state,
   resolve_update_break_moved_away,
   resolve_update_raise_moved_away,
   resolve_update_moved_away_node,
   resolve_incoming_add_ignore,
   resolve_merge_incoming_added_file_text_update,
   resolve_merge_incoming_added_file_text_merge,
   resolve_merge_incoming_added_file_replace_and_merge,
   resolve_merge_incoming_added_dir_merge,
   resolve_update_incoming_added_dir_merge,
   merge_incoming_added_dir_replace,
   resolve_incoming_delete_ignore,
   resolve_incoming_delete_accept,
   resolve_incoming_move_file_text_merge,
   resolve_both_moved_file_text_merge,
   resolve_both_moved_dir_merge,
   resolve_both_moved_dir_move_merge,
   resolve_incoming_move_dir_merge,
   resolve_local_move_file_merge,
   resolve_local_move_dir_merge,
   resolve_both_moved_file_update_keep_local_move,
   resolve_both_moved_file_update_keep_incoming_move):
   Use the new helper functions.

* subversion/libsvn_client/diff.c
  (diff_wc_wc, diff_repos_wc): Call svn_client__textbase_sync().

* subversion/libsvn_client/resolved.c
  (svn_client__resolve_conflicts): Call svn_client__textbase_sync().
  (resolve_locked): New helper factored out from svn_client_resolve().
   Call svn_client__textbase_sync().
  (svn_client_resolve): Use the new helper function.

* subversion/libsvn_client/revert.c
  (revert): Call svn_client__textbase_sync().

* subversion/libsvn_client/switch.c
  (switch_internal): Call svn_client__textbase_sync().

* subversion/libsvn_client/update.c
  (update_internal): Call svn_client__textbase_sync().

* subversion/libsvn_client/upgrade.c
  (svn_client_upgrade): Call svn_client__textbase_sync() after the upgrade
   upgrade with `allow_dehydrate` set to TRUE.  Do this to have a predictable
   state for the next operation after upgrade.

* subversion/libsvn_wc/wc_db.h
  (svn_wc__db_pristine_prepare_install): Accept a new `hydrated` parameter.
  (svn_wc__db_pristine_check): Return the new part of the state in the
   `hydrated` out-parameter.
  (svn_wc__db_pristine_dehydrate): Declare this new function.
  (svn_wc__db_textbase_walk_cb_t,
   svn_wc__db_textbase_hydrate_cb_t): Declare these new callback types.
  (svn_wc__db_textbase_walk,
   svn_wc__db_textbase_sync): Declare these new functions.

* subversion/libsvn_wc/wc-metadata.sql
  (STMT_CREATE_SCHEMA, STMT_UPGRADE_TO_32): Make the schema changes:
   - Add the 'hydrated' column to the PRISTINE table.
   - Add the I_PRISTINE_UNREFERENCED index.
   - Add the TEXTBASE_REFS table, adjust the triggers in NODES to update
     the TEXTBASE_REFS.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_INSERT_OR_IGNORE_PRISTINE): Set the value of the `hydrated` column.
  (STMT_INSERT_PRISTINE): Replace with …
  (STMT_UPSERT_PRISTINE): …this new statement.
  (STMT_SELECT_PRISTINE): Select the value of the `hydrated` column.
  (STMT_SELECT_COPY_PRISTINES): Select the value of the `hydrated` column.
  (STMT_UPDATE_PRISTINE_HYDRATED): New statement.
  (STMT_TEXTBASE_ADD_REF, STMT_TEXTBASE_REMOVE_REF,
   STMT_TEXTBASE_WALK, STMT_TEXTBASE_SYNC): New statements.

* subversion/libsvn_wc/wc_db_pristine.c
  (pristine_read_txn): Use the STMT_SELECT_PRISTINE statement.
   Return NULL if the pristine contents are currently not available on disk.
  (struct svn_wc__db_install_data_t): Move this declaration, as we're going
   to need it …
  (install_stream_write_fn,
   install_stream_seek_fn,
   install_stream_close_fn): …in these functions that implement a new install
   stream wrapper.  Use these new functions …
  (svn_wc__db_pristine_prepare_install): …here, to handle cases where we
   are going to install the pristine without creating the file on disk.
  (pristine_install_txn): Accept an svn_wc__db_install_data_t object.
   Ensure that we do not inadvertently dehydrate an existing hydrated entry.
   Drop the file-related debug checks, at least for now.
  (svn_wc__db_pristine_install): Adjust call to pristine_install_txn().
  (svn_wc__db_pristine_install_abort): Update to handle a case where the
   pristine is installed without the on-disk contents.
  (maybe_transfer_one_pristine): Accept and use the new `hydrated` parameter.
  (pristine_transfer_txn): Pass the `hydrated` column value when calling the
   maybe_transfer_one_pristine() function.
  (pristine_remove_if_unreferenced_txn): Remove the file with ignore_enoent=
   TRUE in all cases.
  (svn_wc__db_pristine_check): Fetch the new `hydrated` parameter.
   Drop the optimization that stats the file first and always consult
   the database, at least for now.
  (svn_wc__db_pristine_dehydrate): Implement this new function.

* subversion/libsvn_wc/wc_db_textbase.c: New file with the implementation of
  the svn_wc__db_textbase_walk() and svn_wc__db_textbase_sync() functions.

* subversion/include/private/svn_wc_private.h
  (svn_wc__textbase_hydrate_cb_t): New callback type.
  (svn_wc__textbase_sync): Declare.

* subversion/libsvn_wc/textbase.h
  (svn_wc__textbase_prepare_install): Accept a new `hydrated` argument,
   allowing the caller to provide a hint to optimize away writing the
   new contents to disk.  This is somewhat transitional, as ideally we'd
   probably want to be able to determine that from the textbase layer.

* subversion/libsvn_wc/textbase.c
  (svn_wc__textbase_prepare_install): Forward the new argument to
   the svn_wc__db layer.
  (struct textbase_sync_baton_t): Declare.
  (textbase_walk_cb, textbase_hydrate_cb): Implement these new callbacks.
  (svn_wc__textbase_sync): New function, internally calls the new
   svn_wc__db_textbase_walk() and svn_wc__db_textbase_sync() functions.

* subversion/libsvn_wc/adm_crawler.c
  (svn_wc__internal_transmit_text_deltas): Pass hydrated=FALSE when preparing
   to install the new text-base.

* subversion/libsvn_wc/adm_ops.c
  (svn_wc__get_pristine_contents_by_checksum): Check to see if the pristine
   contents are both present and hydrated.
  (get_pristine_lazyopen_func): Adjust the call to svn_wc__db_pristine_read()
   and handle a case where the pristine is not hydrated with an error.

* subversion/libsvn_wc/externals.c
  (apply_textdelta): Pass hydrated=TRUE when preparing to install the new
   text-base, as we're going to use this contents in close_file().
  (close_file): Adjust the call to svn_wc__db_pristine_read() and handle
   a case where the pristine is not hydrated with an error.

* subversion/libsvn_wc/update_editor.c
  (lazy_open_target, svn_wc_add_repos_file4): Determine and pass the value of
   the new `hydrated` parameter when preparing to install the new text-base.

* subversion/libsvn_wc/wc.h
  (SVN_WC__VERSION): Bump the format version to 32.

* subversion/libsvn_wc/upgrade.c
  (migrate_text_bases): Pass the new `hydrated` value when executing the
   STMT_INSERT_OR_IGNORE_PRISTINE statement.
  (bump_to_32): New function, called …
  (svn_wc__upgrade_sdb): …here.

* subversion/tests/cmdline/authz_tests.py
  (remove_access_after_commit): Mark as work-in-progress.  The current
   behavior is that we fail to fetch the text-base due to the added authz
   restriction.  Need to see if we want to have special handling for
   this case or if the test expectation needs to change.

* subversion/tests/cmdline/basic_tests.py
  (basic_commit_corruption, basic_update_corruption): Mark as work-in-progress.
   These tests perform white-box checks with svntest.wc.text_base_path().

* subversion/tests/cmdline/diff_tests.py
  (diff_external_diffcmd): Mark as work-in-progress.
   The test performs a white-box check with svntest.wc.text_base_path().

* subversion/tests/cmdline/basic_tests.py
  (revert_reexpand_keyword): Mark as XFail.
   Walking the text-bases automatically repairs timestamps, so now the
   first and the second reverts in this test behave identically, as if
   'svn cleanup' or any other command that repairs the timestamps had been
   called beforehand.  Judging by the second part of the test, we're fine
   with revert doing nothing in that case, but that essentially contradicts
   the expectation in its first part.

* subversion/tests/cmdline/trans_tests.py
  (keywords_from_birth, eol_change_is_text_mod): Mark as work-in-progress.
   These tests perform white-box checks with svntest.wc.text_base_path().

* subversion/tests/cmdline/update_tests.py
  (skip_access_denied): Mark as work-in-progress.  Properly handling access
   denied is going to require some additional work, because the error now
   happens when we try to use the working file as the base in update_editor.c:
   apply_textdelta().
  (missing_tmp_update): Mark as work-in-progress.  Need to see if we need
   to do something beyond updating the test expectation, as we now receive
   a different error.

* subversion/tests/cmdline/upgrade_tests.py
  (check_pristine): Temporarily disable the white-box checks with
   svntest.wc.text_base_path().
  (check_dav_cache): Check for SQLite >= 3.9.0, as we started using an
   index on expression in the working copy schema.
  (replaced_files): Mark as work-in-progress.  We need to fetch the missing
   pristines, but the test uses a prepared working copy that points to
   file:///tmp/repo.
  (auto_analyze): Mark as work-in-progress.  The test calls status on
   a non-upgraded wc.

* subversion/tests/libsvn_wc/db-test.c
  (TESTING_DATA): Adjust the query to set the value of the new
   `hydrated` column.

* subversion/tests/libsvn_wc/entries-compat.c
  (TESTING_DATA): Adjust the query to set the value of the new
   `hydrated` column.

* subversion/tests/libsvn_wc/pristine-store-test.c
  (pristine_write_read,
   pristine_delete_while_open,
   reject_mismatching_text): Adjust calls to the updated svn_wc__db_pristine
   functions.
  (pristine_install_dehydrated, pristine_dehydrate): New tests.
  (test_funcs): Run the new tests.

* subversion/tests/libsvn_wc/utils.c
  (sbox_wc_revert, sbox_wc_resolve): Use the libsvn_client API in these
   sandbox utils, as we already do in sbox_wc_update(), sbox_wc_commit(),
   sbox_wc_move(), sbox_wc_switch(), etc.

* subversion/tests/libsvn_wc/wc-queries-test.c
  (slow_statements): Drop STMT_SELECT_UNREFERENCED_PRISTINES.
   Add STMT_TEXTBASE_SYNC.  Note that currently the last statement
   uses a temporary B-tree for GROUP BY.

* subversion/tests/libsvn_wc/wc-test-queries.sql
  (STMT_ENSURE_EMPTY_PRISTINE): Adjust the query to set the value of
   the new `hydrated` column.

Added:
    subversion/branches/pristines-on-demand/subversion/libsvn_client/textbase.c 
  (with props)
    
subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_textbase.c   
(with props)
Modified:
    
subversion/branches/pristines-on-demand/subversion/include/private/svn_wc_private.h
    subversion/branches/pristines-on-demand/subversion/libsvn_client/cat.c
    subversion/branches/pristines-on-demand/subversion/libsvn_client/client.h
    subversion/branches/pristines-on-demand/subversion/libsvn_client/commit.c
    subversion/branches/pristines-on-demand/subversion/libsvn_client/conflicts.c
    subversion/branches/pristines-on-demand/subversion/libsvn_client/diff.c
    subversion/branches/pristines-on-demand/subversion/libsvn_client/resolved.c
    subversion/branches/pristines-on-demand/subversion/libsvn_client/revert.c
    subversion/branches/pristines-on-demand/subversion/libsvn_client/switch.c
    subversion/branches/pristines-on-demand/subversion/libsvn_client/update.c
    subversion/branches/pristines-on-demand/subversion/libsvn_client/upgrade.c
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_crawler.c
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_ops.c
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/externals.c
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.h
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/update_editor.c
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/upgrade.c
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-metadata.sql
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc-queries.sql
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc.h
    subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db.h
    
subversion/branches/pristines-on-demand/subversion/libsvn_wc/wc_db_pristine.c
    
subversion/branches/pristines-on-demand/subversion/tests/cmdline/authz_tests.py
    
subversion/branches/pristines-on-demand/subversion/tests/cmdline/basic_tests.py
    
subversion/branches/pristines-on-demand/subversion/tests/cmdline/diff_tests.py
    
subversion/branches/pristines-on-demand/subversion/tests/cmdline/revert_tests.py
    
subversion/branches/pristines-on-demand/subversion/tests/cmdline/trans_tests.py
    
subversion/branches/pristines-on-demand/subversion/tests/cmdline/update_tests.py
    
subversion/branches/pristines-on-demand/subversion/tests/cmdline/upgrade_tests.py
    subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/db-test.c
    
subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/entries-compat.c
    
subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/pristine-store-test.c
    subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/utils.c
    
subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-queries-test.c
    
subversion/branches/pristines-on-demand/subversion/tests/libsvn_wc/wc-test-queries.sql

Modified: 
subversion/branches/pristines-on-demand/subversion/include/private/svn_wc_private.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/include/private/svn_wc_private.h?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- 
subversion/branches/pristines-on-demand/subversion/include/private/svn_wc_private.h
 (original)
+++ 
subversion/branches/pristines-on-demand/subversion/include/private/svn_wc_private.h
 Fri Aug 27 13:54:33 2021
@@ -2179,6 +2179,33 @@ svn_error_t *
 svn_wc__working_file_writer_close(svn_wc__working_file_writer_t *writer);
 
 
+/* The callback invoked by svn_wc__textbase_sync() to fetch the text-base
+   contents identified by REPOS_ROOT_URL, REPOS_RELPATH and REVISION. */
+typedef svn_error_t *(*svn_wc__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-base contents for the LOCAL_ABSPATH tree.
+   If ALLOW_HYDRATE is true, fetch the required 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 not required. */
+svn_error_t *
+svn_wc__textbase_sync(svn_wc_context_t *wc_ctx,
+                      const char *local_abspath,
+                      svn_boolean_t allow_hydrate,
+                      svn_boolean_t allow_dehydrate,
+                      svn_wc__textbase_hydrate_cb_t hydrate_callback,
+                      void *hydrate_baton,
+                      svn_cancel_func_t cancel_func,
+                      void *cancel_baton,
+                      apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/pristines-on-demand/subversion/libsvn_client/cat.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/cat.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/cat.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/cat.c Fri 
Aug 27 13:54:33 2021
@@ -216,6 +216,10 @@ svn_client_cat3(apr_hash_t **returned_pr
 
       SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
                                       scratch_pool));
+
+      SVN_ERR(svn_client__textbase_sync(local_abspath, TRUE, TRUE,
+                                        ctx, scratch_pool));
+
       SVN_ERR(svn_client__get_normalized_stream(&normal_stream, ctx->wc_ctx,
                                             local_abspath, revision,
                                             expand_keywords, FALSE,
@@ -229,9 +233,14 @@ svn_client_cat3(apr_hash_t **returned_pr
         SVN_ERR(svn_wc_prop_list2(returned_props, ctx->wc_ctx, local_abspath,
                                   result_pool, scratch_pool));
 
-      return svn_error_trace(svn_stream_copy3(normal_stream, output,
-                                              ctx->cancel_func,
-                                              ctx->cancel_baton, 
scratch_pool));
+      SVN_ERR(svn_stream_copy3(normal_stream, output,
+                               ctx->cancel_func,
+                               ctx->cancel_baton, scratch_pool));
+
+      SVN_ERR(svn_client__textbase_sync(local_abspath, FALSE, TRUE,
+                                        ctx, scratch_pool));
+
+      return SVN_NO_ERROR;
     }
 
   /* Get an RA plugin for this filesystem object. */

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/client.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/client.h?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/client.h 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/client.h 
Fri Aug 27 13:54:33 2021
@@ -1236,6 +1236,15 @@ svn_client__merge_locked(svn_client__con
                          apr_pool_t *result_pool,
                          apr_pool_t *scratch_pool);
 
+/* Synchronize the state of the text-base contents for the LOCAL_ABSPATH tree.
+ * Internally this calls svn_wc__textbase_sync(), which see for details. */
+svn_error_t *
+svn_client__textbase_sync(const char *local_abspath,
+                          svn_boolean_t allow_hydrate,
+                          svn_boolean_t allow_dehydrate,
+                          svn_client_ctx_t *ctx,
+                          apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/commit.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/commit.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/commit.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/commit.c 
Fri Aug 27 13:54:33 2021
@@ -728,6 +728,12 @@ svn_client_commit6(const apr_array_heade
       if (cmt_err)
         goto cleanup;
 
+      cmt_err = svn_error_trace(
+                    svn_client__textbase_sync(lock_root, TRUE, TRUE,
+                                              ctx, iterpool));
+      if (cmt_err)
+        goto cleanup;
+
       APR_ARRAY_PUSH(locks_obtained, const char *) = lock_root;
     }
 
@@ -1075,6 +1081,11 @@ svn_client_commit6(const apr_array_heade
           svn_pool_clear(iterpool);
 
           unlock_err = svn_error_compose_create(
+                           svn_client__textbase_sync(lock_root, FALSE, TRUE,
+                                                     ctx, iterpool),
+                           unlock_err);
+
+          unlock_err = svn_error_compose_create(
                            svn_wc__release_write_lock(ctx->wc_ctx, lock_root,
                                                       iterpool),
                            unlock_err);

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/conflicts.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/conflicts.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- 
subversion/branches/pristines-on-demand/subversion/libsvn_client/conflicts.c 
(original)
+++ 
subversion/branches/pristines-on-demand/subversion/libsvn_client/conflicts.c 
Fri Aug 27 13:54:33 2021
@@ -6411,6 +6411,52 @@ resolve_postpone(svn_client_conflict_opt
   return SVN_NO_ERROR; /* Nothing to do. */
 }
 
+static svn_error_t *
+begin_resolve(const char **root_abspath_p,
+              const char *local_abspath,
+              svn_client_ctx_t *ctx,
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool)
+{
+  const char *lock_abspath;
+  svn_error_t *err;
+
+  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
+                                                 local_abspath,
+                                                 result_pool, scratch_pool));
+
+  err = svn_client__textbase_sync(lock_abspath, TRUE, TRUE, ctx, scratch_pool);
+  if (err)
+    {
+      return svn_error_compose_create(
+               err,
+               svn_wc__release_write_lock(ctx->wc_ctx, lock_abspath,
+                                          scratch_pool));
+    }
+
+  *root_abspath_p = lock_abspath;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+finish_resolve(const char *root_abspath,
+               svn_client_ctx_t *ctx,
+               svn_error_t *resolve_err,
+               apr_pool_t *scratch_pool)
+{
+  svn_error_t *err = resolve_err;
+
+  err = svn_error_compose_create(
+          err,
+          svn_client__textbase_sync(root_abspath, FALSE, TRUE, ctx, 
scratch_pool));
+
+  err = svn_error_compose_create(
+          err,
+          svn_wc__release_write_lock(ctx->wc_ctx, root_abspath, scratch_pool));
+
+  return err;
+}
+
 /* Implements conflict_option_resolve_func_t. */
 static svn_error_t *
 resolve_text_conflict(svn_client_conflict_option_t *option,
@@ -6428,9 +6474,8 @@ resolve_text_conflict(svn_client_conflic
   conflict_choice = conflict_option_id_to_wc_conflict_choice(option_id);
   local_abspath = svn_client_conflict_get_local_abspath(conflict);
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
   err = svn_wc__conflict_text_mark_resolved(ctx->wc_ctx,
                                             local_abspath,
                                             conflict_choice,
@@ -6439,9 +6484,7 @@ resolve_text_conflict(svn_client_conflic
                                             ctx->notify_func2,
                                             ctx->notify_baton2,
                                             scratch_pool);
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
+  err = finish_resolve(lock_abspath, ctx, err, scratch_pool);
   svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
   SVN_ERR(err);
 
@@ -6474,18 +6517,15 @@ resolve_prop_conflict(svn_client_conflic
   else
     merged_value = NULL;
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
   err = svn_wc__conflict_prop_mark_resolved(ctx->wc_ctx, local_abspath,
                                             propname, conflict_choice,
                                             merged_value,
                                             ctx->notify_func2,
                                             ctx->notify_baton2,
                                             scratch_pool);
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
+  err = finish_resolve(lock_abspath, ctx, err, scratch_pool);
   svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
   SVN_ERR(err);
 
@@ -6550,9 +6590,8 @@ resolve_accept_current_wc_state(svn_clie
                              svn_dirent_local_style(local_abspath,
                                                     scratch_pool));
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
 
   /* Resolve to current working copy state. */
   err = svn_wc__del_tree_conflict(ctx->wc_ctx, local_abspath, scratch_pool);
@@ -6565,10 +6604,7 @@ resolve_accept_current_wc_state(svn_clie
                                            scratch_pool),
                       scratch_pool);
 
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   conflict->resolution_tree = option_id;
 
@@ -6588,9 +6624,8 @@ resolve_update_break_moved_away(svn_clie
 
   local_abspath = svn_client_conflict_get_local_abspath(conflict);
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
   err = svn_wc__conflict_tree_update_break_moved_away(ctx->wc_ctx,
                                                       local_abspath,
                                                       ctx->cancel_func,
@@ -6598,10 +6633,7 @@ resolve_update_break_moved_away(svn_clie
                                                       ctx->notify_func2,
                                                       ctx->notify_baton2,
                                                       scratch_pool);
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   conflict->resolution_tree = svn_client_conflict_option_get_id(option);
 
@@ -6621,9 +6653,8 @@ resolve_update_raise_moved_away(svn_clie
 
   local_abspath = svn_client_conflict_get_local_abspath(conflict);
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
   err = svn_wc__conflict_tree_update_raise_moved_away(ctx->wc_ctx,
                                                       local_abspath,
                                                       ctx->cancel_func,
@@ -6631,10 +6662,7 @@ resolve_update_raise_moved_away(svn_clie
                                                       ctx->notify_func2,
                                                       ctx->notify_baton2,
                                                       scratch_pool);
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   conflict->resolution_tree = svn_client_conflict_option_get_id(option);
 
@@ -6654,9 +6682,8 @@ resolve_update_moved_away_node(svn_clien
 
   local_abspath = svn_client_conflict_get_local_abspath(conflict);
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
   err = svn_wc__conflict_tree_update_moved_away_node(ctx->wc_ctx,
                                                      local_abspath,
                                                      ctx->cancel_func,
@@ -6664,9 +6691,7 @@ resolve_update_moved_away_node(svn_clien
                                                      ctx->notify_func2,
                                                      ctx->notify_baton2,
                                                      scratch_pool);
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
+  err = finish_resolve(lock_abspath, ctx, err, scratch_pool);
   svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
   SVN_ERR(err);
 
@@ -6838,9 +6863,8 @@ resolve_incoming_add_ignore(svn_client_c
   local_abspath = svn_client_conflict_get_local_abspath(conflict);
   operation = svn_client_conflict_get_operation(conflict);
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
 
   if (operation == svn_wc_operation_update)
     {
@@ -6865,10 +6889,7 @@ resolve_incoming_add_ignore(svn_client_c
                       scratch_pool);
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   conflict->resolution_tree = svn_client_conflict_option_get_id(option);
 
@@ -6962,9 +6983,8 @@ resolve_merge_incoming_added_file_text_u
                          apr_hash_make(scratch_pool), scratch_pool));
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
 
   /* Revert the path in order to restore the repository's line of
    * history, which is part of the BASE tree. This revert operation
@@ -6997,10 +7017,7 @@ unlock_wc:
               err, _("If needed, a backup copy of '%s' can be found at '%s'"),
               svn_dirent_local_style(local_abspath, scratch_pool),
               svn_dirent_local_style(working_file_tmp_abspath, scratch_pool));
-  err = svn_error_compose_create(err,
-                                 svn_wc__release_write_lock(ctx->wc_ctx,
-                                                            lock_abspath,
-                                                            scratch_pool));
+  err = finish_resolve(lock_abspath, ctx, err, scratch_pool);
   svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
   SVN_ERR(err);
 
@@ -7108,16 +7125,12 @@ resolve_merge_incoming_added_file_text_m
                          apr_hash_make(scratch_pool), scratch_pool));
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
   /* Resolve to current working copy state. svn_wc_merge5() requires this. */
   err = svn_wc__del_tree_conflict(ctx->wc_ctx, local_abspath, scratch_pool);
   if (err)
-    return svn_error_compose_create(err,
-                                    svn_wc__release_write_lock(ctx->wc_ctx,
-                                                               lock_abspath,
-                                                               scratch_pool));
+    return finish_resolve(lock_abspath, ctx, err, scratch_pool);
   /* Perform the file merge. ### Merge into tempfile and then rename on top? */
   err = svn_wc_merge5(&merge_content_outcome, &merge_props_outcome,
                       ctx->wc_ctx, empty_file_abspath,
@@ -7130,9 +7143,7 @@ resolve_merge_incoming_added_file_text_m
                       NULL, NULL, /* conflict func/baton */
                       NULL, NULL, /* don't allow user to cancel here */
                       scratch_pool);
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
+  err = finish_resolve(lock_abspath, ctx, err, scratch_pool);
   svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
   SVN_ERR(err);
 
@@ -7247,9 +7258,8 @@ resolve_merge_incoming_added_file_replac
   /* Reset the stream in preparation for adding its content to WC. */
   SVN_ERR(svn_stream_reset(incoming_new_stream));
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
 
   /* ### The following WC modifications should be atomic. */
 
@@ -7334,9 +7344,7 @@ resolve_merge_incoming_added_file_replac
     }
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
+  err = finish_resolve(lock_abspath, ctx, err, scratch_pool);
   svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
   SVN_ERR(err);
 
@@ -7908,9 +7916,8 @@ resolve_merge_incoming_added_dir_merge(s
     }
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
 
   /* ### wrap in a transaction */
   err = merge_newly_added_dir(added_repos_relpath,
@@ -7921,9 +7928,7 @@ resolve_merge_incoming_added_dir_merge(s
   if (!err)
     err = svn_wc__del_tree_conflict(ctx->wc_ctx, local_abspath, scratch_pool);
 
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
+  err = finish_resolve(lock_abspath, ctx, err, scratch_pool);
   svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
   SVN_ERR(err);
 
@@ -7957,9 +7962,8 @@ resolve_update_incoming_added_dir_merge(
   if (local_change == svn_wc_conflict_reason_unversioned)
     {
       char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
-      SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-                &lock_abspath, ctx->wc_ctx, parent_abspath,
-                scratch_pool, scratch_pool));
+      SVN_ERR(begin_resolve(&lock_abspath, parent_abspath, ctx,
+                            scratch_pool, scratch_pool));
 
       /* The update/switch operation has added the incoming versioned
        * directory as a deleted op-depth layer. We can revert this layer
@@ -7979,9 +7983,8 @@ resolve_update_incoming_added_dir_merge(
     }
   else
     {
-      SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-                &lock_abspath, ctx->wc_ctx, local_abspath,
-                scratch_pool, scratch_pool));
+      SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                            scratch_pool, scratch_pool));
       err = svn_wc__conflict_tree_update_local_add(ctx->wc_ctx,
                                                    local_abspath,
                                                    ctx->cancel_func,
@@ -7991,10 +7994,7 @@ resolve_update_incoming_added_dir_merge(
                                                    scratch_pool);
     }
 
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -8041,11 +8041,10 @@ merge_incoming_added_dir_replace(svn_cli
 
   /* ### The following WC modifications should be atomic. */
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 svn_dirent_dirname(
-                                                   local_abspath,
-                                                   scratch_pool),
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        svn_dirent_dirname(local_abspath,
+                                           scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
   /* Remove the working directory. */
   err = svn_wc_delete4(ctx->wc_ctx, local_abspath, FALSE, FALSE,
@@ -8136,9 +8135,7 @@ merge_incoming_added_dir_replace(svn_cli
     }
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
+  err = finish_resolve(lock_abspath, ctx, err, scratch_pool);
   svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
   SVN_ERR(err);
 
@@ -8388,9 +8385,8 @@ resolve_incoming_delete_ignore(svn_clien
   option_id = svn_client_conflict_option_get_id(option);
   local_abspath = svn_client_conflict_get_local_abspath(conflict);
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 local_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, local_abspath, ctx,
+                        scratch_pool, scratch_pool));
 
   err = verify_local_state_for_incoming_delete(conflict, option, ctx,
                                                scratch_pool);
@@ -8409,10 +8405,7 @@ resolve_incoming_delete_ignore(svn_clien
                       scratch_pool);
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   conflict->resolution_tree = option_id;
 
@@ -8437,9 +8430,8 @@ resolve_incoming_delete_accept(svn_clien
 
   /* Deleting a node requires a lock on the node's parent. */
   parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
-                                                 parent_abspath,
-                                                 scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath, parent_abspath, ctx,
+                        scratch_pool, scratch_pool));
 
   err = verify_local_state_for_incoming_delete(conflict, option, ctx,
                                                scratch_pool);
@@ -8478,10 +8470,7 @@ resolve_incoming_delete_accept(svn_clien
                       scratch_pool);
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   conflict->resolution_tree = option_id;
 
@@ -8616,12 +8605,11 @@ resolve_incoming_move_file_text_merge(sv
     merge_source_abspath = victim_abspath;
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(victim_abspath,
-                                            moved_to_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        svn_dirent_get_longest_ancestor(victim_abspath,
+                                                        moved_to_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
   if (local_change != svn_wc_conflict_reason_missing)
     {
@@ -8817,10 +8805,7 @@ unlock_wc:
               err, _("If needed, a backup copy of '%s' can be found at '%s'"),
               svn_dirent_local_style(moved_to_abspath, scratch_pool),
               svn_dirent_local_style(incoming_abspath, scratch_pool));
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -8934,12 +8919,11 @@ resolve_both_moved_file_text_merge(svn_c
     APR_ARRAY_IDX(local_moves, local_details->wc_move_target_idx, const char 
*);
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(victim_abspath,
-                                            local_moved_to_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        svn_dirent_get_longest_ancestor(victim_abspath,
+                                                        local_moved_to_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
    /* Get a copy of the incoming moved item's properties. */
   err = svn_wc_prop_list2(&incoming_props, ctx->wc_ctx,
@@ -9022,10 +9006,7 @@ resolve_both_moved_file_text_merge(svn_c
   conflict->resolution_tree = option_id;
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -9109,12 +9090,11 @@ resolve_both_moved_dir_merge(svn_client_
     APR_ARRAY_IDX(local_moves, local_details->wc_move_target_idx, const char 
*);
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(victim_abspath,
-                                            local_moved_to_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        svn_dirent_get_longest_ancestor(victim_abspath,
+                                                        local_moved_to_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
   /* Perform the merge. */
   incoming_old_url = apr_pstrcat(scratch_pool, repos_root_url, "/",
@@ -9170,10 +9150,7 @@ resolve_both_moved_dir_merge(svn_client_
   conflict->resolution_tree = option_id;
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -9258,12 +9235,11 @@ resolve_both_moved_dir_move_merge(svn_cl
     APR_ARRAY_IDX(local_moves, local_details->wc_move_target_idx, const char 
*);
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(victim_abspath,
-                                            local_moved_to_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        svn_dirent_get_longest_ancestor(victim_abspath,
+                                                        local_moved_to_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
   /* Revert the incoming move target directory. */
   err = svn_wc_revert6(ctx->wc_ctx, incoming_moved_to_abspath,
@@ -9332,10 +9308,7 @@ resolve_both_moved_dir_move_merge(svn_cl
   conflict->resolution_tree = option_id;
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -9420,12 +9393,11 @@ resolve_incoming_move_dir_merge(svn_clie
 
   /* ### The following WC modifications should be atomic. */
 
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(local_abspath,
-                                            moved_to_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        svn_dirent_get_longest_ancestor(local_abspath,
+                                                        moved_to_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
   err = svn_wc__node_get_origin(&is_copy, &moved_to_peg_rev,
                                 &moved_to_repos_relpath,
@@ -9564,10 +9536,7 @@ resolve_incoming_move_dir_merge(svn_clie
   conflict->resolution_tree = option_id;
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -9689,12 +9658,11 @@ resolve_local_move_file_merge(svn_client
                          scratch_pool));
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(conflict->local_abspath,
-                                            merge_target_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        
svn_dirent_get_longest_ancestor(conflict->local_abspath,
+                                                        merge_target_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
   /* Perform the file merge. */
   err = svn_wc_merge5(&merge_content_outcome, &merge_props_outcome,
@@ -9712,19 +9680,11 @@ resolve_local_move_file_merge(svn_client
                       scratch_pool);
   svn_io_sleep_for_timestamps(merge_target_abspath, scratch_pool);
   if (err)
-    return svn_error_compose_create(err,
-                                    svn_wc__release_write_lock(ctx->wc_ctx,
-                                                               lock_abspath,
-                                                               scratch_pool));
+    return finish_resolve(lock_abspath, ctx, err, scratch_pool);
 
   err = svn_wc__del_tree_conflict(ctx->wc_ctx, conflict->local_abspath,
                                   scratch_pool);
-  err = svn_error_compose_create(err,
-                                 svn_wc__release_write_lock(ctx->wc_ctx,
-                                                            lock_abspath,
-                                                            scratch_pool));
-  if (err)
-    return svn_error_trace(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   if (ctx->notify_func2)
     {
@@ -9805,12 +9765,11 @@ resolve_local_move_dir_merge(svn_client_
                                          const char *);
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(conflict->local_abspath,
-                                            merge_target_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        
svn_dirent_get_longest_ancestor(conflict->local_abspath,
+                                                        merge_target_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
   /* Resolve to current working copy state.
    * svn_client__merge_locked() requires this. */
@@ -9841,12 +9800,7 @@ resolve_local_move_dir_merge(svn_client_
                                  NULL, ctx, scratch_pool, scratch_pool);
 unlock_wc:
   svn_io_sleep_for_timestamps(merge_target_abspath, scratch_pool);
-  err = svn_error_compose_create(err,
-                                 svn_wc__release_write_lock(ctx->wc_ctx,
-                                                            lock_abspath,
-                                                            scratch_pool));
-  if (err)
-    return svn_error_trace(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   if (ctx->notify_func2)
     {
@@ -11232,12 +11186,11 @@ resolve_both_moved_file_update_keep_loca
                   local_details->preferred_move_target_idx, const char *);
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(victim_abspath,
-                                            local_moved_to_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        svn_dirent_get_longest_ancestor(victim_abspath,
+                                                        local_moved_to_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
    /* Get a copy of the incoming moved item's properties. */
   err = svn_wc_prop_list2(&incoming_props, ctx->wc_ctx,
@@ -11326,10 +11279,7 @@ resolve_both_moved_file_update_keep_loca
   conflict->resolution_tree = option_id;
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -11443,12 +11393,11 @@ resolve_both_moved_file_update_keep_inco
                   local_details->preferred_move_target_idx, const char *);
 
   /* ### The following WC modifications should be atomic. */
-  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
-            &lock_abspath, ctx->wc_ctx,
-            svn_dirent_get_longest_ancestor(victim_abspath,
-                                            local_moved_to_abspath,
-                                            scratch_pool),
-            scratch_pool, scratch_pool));
+  SVN_ERR(begin_resolve(&lock_abspath,
+                        svn_dirent_get_longest_ancestor(victim_abspath,
+                                                        local_moved_to_abspath,
+                                                        scratch_pool),
+                        ctx, scratch_pool, scratch_pool));
 
    /* Get a copy of the incoming moved item's properties. */
   err = svn_wc_prop_list2(&incoming_props, ctx->wc_ctx,
@@ -11532,10 +11481,7 @@ resolve_both_moved_file_update_keep_inco
   conflict->resolution_tree = option_id;
 
 unlock_wc:
-  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
-                                                                 lock_abspath,
-                                                                 
scratch_pool));
-  SVN_ERR(err);
+  SVN_ERR(finish_resolve(lock_abspath, ctx, err, scratch_pool));
 
   return SVN_NO_ERROR;
 }

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/diff.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/diff.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/diff.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/diff.c Fri 
Aug 27 13:54:33 2021
@@ -1866,12 +1866,17 @@ diff_wc_wc(const char *path1,
                           "or between the working versions of two paths"
                           )));
 
+  SVN_ERR(svn_client__textbase_sync(abspath1, TRUE, TRUE, ctx, scratch_pool));
+
   SVN_ERR(svn_wc__diff7(TRUE,
                         ctx->wc_ctx, abspath1, depth,
                         ignore_ancestry, changelists,
                         diff_processor,
                         ctx->cancel_func, ctx->cancel_baton,
                         result_pool, scratch_pool));
+
+  SVN_ERR(svn_client__textbase_sync(abspath1, FALSE, TRUE, ctx, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -2135,6 +2140,8 @@ diff_repos_wc(struct diff_driver_info_t
 
   SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, scratch_pool));
 
+  SVN_ERR(svn_client__textbase_sync(abspath2, TRUE, TRUE, ctx, scratch_pool));
+
   /* Check if our diff target is a copied node. */
   SVN_ERR(svn_wc__node_get_origin(&is_copy,
                                   &cf_revision,
@@ -2340,6 +2347,8 @@ diff_repos_wc(struct diff_driver_info_t
                                       scratch_pool));
     }
 
+  SVN_ERR(svn_client__textbase_sync(abspath2, FALSE, TRUE, ctx, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/resolved.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/resolved.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/resolved.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/resolved.c 
Fri Aug 27 13:54:33 2021
@@ -66,6 +66,10 @@ svn_client__resolve_conflicts(svn_boolea
       const char *local_abspath = APR_ARRAY_IDX(array, i, const char *);
 
       svn_pool_clear(iterpool);
+
+      SVN_ERR(svn_client__textbase_sync(local_abspath, TRUE, TRUE, ctx,
+                                        iterpool));
+
       SVN_ERR(svn_wc__resolve_conflicts(ctx->wc_ctx, local_abspath,
                                         svn_depth_empty,
                                         TRUE /* resolve_text */,
@@ -98,12 +102,44 @@ svn_client__resolve_conflicts(svn_boolea
           if (text_c || prop_c || tree_c)
             *conflicts_remain = TRUE;
         }
+
+      SVN_ERR(svn_client__textbase_sync(local_abspath, FALSE, TRUE, ctx,
+                                        iterpool));
     }
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+resolve_locked(const char *local_abspath,
+               const char *root_abspath,
+               svn_depth_t depth,
+               svn_wc_conflict_choice_t conflict_choice,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *scratch_pool)
+{
+  SVN_ERR(svn_client__textbase_sync(root_abspath, TRUE, TRUE, ctx,
+                                    scratch_pool));
+
+  SVN_ERR(svn_wc__resolve_conflicts(ctx->wc_ctx, local_abspath,
+                                    depth,
+                                    TRUE /* resolve_text */,
+                                    "" /* resolve_prop (ALL props) */,
+                                    TRUE /* resolve_tree */,
+                                    conflict_choice,
+                                    ctx->conflict_func2,
+                                    ctx->conflict_baton2,
+                                    ctx->cancel_func, ctx->cancel_baton,
+                                    ctx->notify_func2, ctx->notify_baton2,
+                                    scratch_pool));
+
+  SVN_ERR(svn_client__textbase_sync(root_abspath, FALSE, TRUE, ctx,
+                                    scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_client_resolve(const char *path,
                    svn_depth_t depth,
@@ -126,17 +162,9 @@ svn_client_resolve(const char *path,
 
   SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
                                                  local_abspath, pool, pool));
-  err = svn_wc__resolve_conflicts(ctx->wc_ctx, local_abspath,
-                                  depth,
-                                  TRUE /* resolve_text */,
-                                  "" /* resolve_prop (ALL props) */,
-                                  TRUE /* resolve_tree */,
-                                  conflict_choice,
-                                  ctx->conflict_func2,
-                                  ctx->conflict_baton2,
-                                  ctx->cancel_func, ctx->cancel_baton,
-                                  ctx->notify_func2, ctx->notify_baton2,
-                                  pool);
+
+  err = resolve_locked(local_abspath, lock_abspath, depth,
+                       conflict_choice, ctx, pool);
 
   err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
                                                                  lock_abspath,

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/revert.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/revert.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/revert.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/revert.c 
Fri Aug 27 13:54:33 2021
@@ -69,6 +69,9 @@ revert(void *baton, apr_pool_t *result_p
   struct revert_with_write_lock_baton *b = baton;
   svn_error_t *err;
 
+  SVN_ERR(svn_client__textbase_sync(b->local_abspath, TRUE, TRUE,
+                                    b->ctx, scratch_pool));
+
   err = svn_wc_revert6(b->ctx->wc_ctx,
                        b->local_abspath,
                        b->depth,
@@ -108,6 +111,9 @@ revert(void *baton, apr_pool_t *result_p
         return svn_error_trace(err);
     }
 
+  SVN_ERR(svn_client__textbase_sync(b->local_abspath, FALSE, TRUE,
+                                    b->ctx, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/switch.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/switch.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/switch.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/switch.c 
Fri Aug 27 13:54:33 2021
@@ -202,6 +202,8 @@ switch_internal(svn_revnum_t *result_rev
                                   pool));
     }
 
+  SVN_ERR(svn_client__textbase_sync(local_abspath, TRUE, TRUE, ctx, pool));
+
   /* Open an RA session to 'source' URL */
   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc,
                                             switch_url, anchor_abspath,
@@ -369,6 +371,8 @@ switch_internal(svn_revnum_t *result_rev
                                            ctx, pool));
     }
 
+  SVN_ERR(svn_client__textbase_sync(local_abspath, FALSE, TRUE, ctx, pool));
+
   /* Let everyone know we're finished here. */
   if (ctx->notify_func2)
     {

Added: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/textbase.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/textbase.c?rev=1892650&view=auto
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/textbase.c 
(added)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/textbase.c 
Fri Aug 27 13:54:33 2021
@@ -0,0 +1,109 @@
+/*
+ * textbase.c:  wrappers around wc text-base functionality
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include "svn_path.h"
+
+#include "private/svn_wc_private.h"
+
+#include "client.h"
+
+/* A baton for use with textbase_hydrate_cb(). */
+typedef struct textbase_hydrate_baton_t
+{
+  apr_pool_t *pool;
+  svn_client_ctx_t *ctx;
+  svn_ra_session_t *ra_session;
+} textbase_hydrate_baton_t;
+
+/* Implements svn_wc__textbase_hydrate_cb_t. */
+static svn_error_t *
+textbase_hydrate_cb(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)
+{
+  struct textbase_hydrate_baton_t *b = baton;
+  const char *url;
+  const char *old_url;
+  svn_error_t *err;
+
+  url = svn_path_url_add_component2(repos_root_url, repos_relpath,
+                                    scratch_pool);
+
+  if (!b->ra_session)
+    {
+      svn_ra_session_t *session;
+
+      /* ### Transitional: open a *new* RA session for every call to
+         svn_client__textbase_sync().
+
+         What we could do here: make a sync context that accepts an optional
+         RA session.  If it's passed-in, use that session.  Else, open a new
+         session, but pass it on to the caller so that it could be reused
+         further on. */
+
+      /* Open the RA session that does not correspond to a working copy.
+         At this point we know that we don't have a local copy of the contents,
+         so rechecking that in get_wc_contents() is just a waste of time. */
+      SVN_ERR(svn_client__open_ra_session_internal(&session, NULL, url, NULL,
+                                                   NULL, FALSE, FALSE, b->ctx,
+                                                   b->pool, scratch_pool));
+      b->ra_session = session;
+    }
+
+  SVN_ERR(svn_client__ensure_ra_session_url(&old_url, b->ra_session, url,
+                                            scratch_pool));
+  err = svn_ra_get_file(b->ra_session, "", revision, contents,
+                        NULL, NULL, scratch_pool);
+  err = svn_error_compose_create(err, svn_stream_close(contents));
+
+  return svn_error_trace(err);
+}
+
+svn_error_t *
+svn_client__textbase_sync(const char *local_abspath,
+                          svn_boolean_t allow_hydrate,
+                          svn_boolean_t allow_dehydrate,
+                          svn_client_ctx_t *ctx,
+                          apr_pool_t *scratch_pool)
+{
+  textbase_hydrate_baton_t baton = {0};
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  baton.pool = scratch_pool;
+  baton.ctx = ctx;
+  baton.ra_session = NULL;
+
+  SVN_ERR(svn_wc__textbase_sync(ctx->wc_ctx, local_abspath,
+                                allow_hydrate, allow_dehydrate,
+                                textbase_hydrate_cb, &baton,
+                                ctx->cancel_func, ctx->cancel_baton,
+                                scratch_pool));
+
+  return SVN_NO_ERROR;
+}

Propchange: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/textbase.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/update.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/update.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/update.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/update.c 
Fri Aug 27 13:54:33 2021
@@ -467,6 +467,9 @@ update_internal(svn_revnum_t *result_rev
       ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
     }
 
+  SVN_ERR(svn_client__textbase_sync(local_abspath, TRUE, TRUE,
+                                    ctx, scratch_pool));
+
   SVN_ERR(reuse_ra_session(ra_session_p, &corrected_url, anchor_url,
                            anchor_abspath, ctx, result_pool, scratch_pool));
   ra_session = *ra_session_p;
@@ -576,6 +579,9 @@ update_internal(svn_revnum_t *result_rev
                                repos_root_url, ra_session, ctx, scratch_pool));
     }
 
+  SVN_ERR(svn_client__textbase_sync(local_abspath, FALSE, TRUE,
+                                    ctx, scratch_pool));
+
   /* Let everyone know we're finished here (unless we're asked not to). */
   if (ctx->notify_func2 && notify_summary)
     {

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_client/upgrade.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_client/upgrade.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_client/upgrade.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_client/upgrade.c 
Fri Aug 27 13:54:33 2021
@@ -176,6 +176,10 @@ svn_client_upgrade(const char *path,
       SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath,
                                                 &info_baton, scratch_pool));
     }
+
+  SVN_ERR(svn_client__textbase_sync(local_abspath, FALSE, TRUE,
+                                    ctx, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_crawler.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_crawler.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_crawler.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_crawler.c 
Fri Aug 27 13:54:33 2021
@@ -1110,7 +1110,7 @@ svn_wc__internal_transmit_text_deltas(sv
       SVN_ERR(svn_wc__textbase_prepare_install(&new_pristine_stream,
                                                &install_data,
                                                &local_sha1_checksum, NULL,
-                                               db, local_abspath,
+                                               db, local_abspath, FALSE,
                                                scratch_pool, scratch_pool));
       local_stream = copying_stream(local_stream, new_pristine_stream,
                                     scratch_pool);

Modified: subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_ops.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_ops.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_ops.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/adm_ops.c Fri 
Aug 27 13:54:33 2021
@@ -827,13 +827,14 @@ typedef struct get_pristine_lazyopen_bat
 
 /* Implements svn_stream_lazyopen_func_t */
 static svn_error_t *
-get_pristine_lazyopen_func(svn_stream_t **stream,
+get_pristine_lazyopen_func(svn_stream_t **stream_p,
                            void *baton,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool)
 {
   get_pristine_lazyopen_baton_t *b = baton;
   const svn_checksum_t *sha1_checksum;
+  svn_stream_t *stream;
 
   /* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5,
      we'll use it to lookup the SHA1. */
@@ -844,9 +845,13 @@ get_pristine_lazyopen_func(svn_stream_t
                                          b->wri_abspath, b->checksum,
                                          scratch_pool, scratch_pool));
 
-  SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db,
+  SVN_ERR(svn_wc__db_pristine_read(&stream, NULL, b->wc_ctx->db,
                                    b->wri_abspath, sha1_checksum,
                                    result_pool, scratch_pool));
+  if (!stream)
+    return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
+
+  *stream_p = stream;
   return SVN_NO_ERROR;
 }
 
@@ -859,13 +864,14 @@ svn_wc__get_pristine_contents_by_checksu
                                           apr_pool_t *scratch_pool)
 {
   svn_boolean_t present;
+  svn_boolean_t hydrated;
 
   *contents = NULL;
 
-  SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath,
-                                    checksum, scratch_pool));
+  SVN_ERR(svn_wc__db_pristine_check(&present, &hydrated, wc_ctx->db,
+                                    wri_abspath, checksum, scratch_pool));
 
-  if (present)
+  if (present && hydrated)
     {
       get_pristine_lazyopen_baton_t *gpl_baton;
 

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_wc/externals.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/externals.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_wc/externals.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/externals.c 
Fri Aug 27 13:54:33 2021
@@ -645,6 +645,7 @@ apply_textdelta(void *file_baton,
                                            &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,
@@ -898,6 +899,9 @@ close_file(void *file_baton,
                                              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,

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.c Fri 
Aug 27 13:54:33 2021
@@ -431,14 +431,113 @@ svn_wc__textbase_prepare_install(svn_str
                                  svn_checksum_t **md5_checksum_p,
                                  svn_wc__db_t *db,
                                  const char *local_abspath,
+                                 svn_boolean_t hydrated,
                                  apr_pool_t *result_pool,
                                  apr_pool_t *scratch_pool)
 {
   SVN_ERR(svn_wc__db_pristine_prepare_install(stream_p, install_data_p,
                                               sha1_checksum_p,
                                               md5_checksum_p,
-                                              db, local_abspath,
+                                              db, local_abspath, hydrated,
                                               result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }
+
+/* A baton for use with textbase_walk_cb() and textbase_sync_cb(). */
+typedef struct textbase_sync_baton_t
+{
+  svn_wc__db_t *db;
+  svn_wc__textbase_hydrate_cb_t hydrate_callback;
+  void *hydrate_baton;
+} textbase_sync_baton_t;
+
+/* Implements svn_wc__db_textbase_walk_cb_t. */
+static svn_error_t *
+textbase_walk_cb(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)
+{
+  textbase_sync_baton_t *b = baton;
+  svn_boolean_t referenced;
+
+  if (op_depth < max_op_depth)
+    {
+      /* Pin the text-base with working changes. */
+      referenced = TRUE;
+    }
+  else
+    {
+      svn_boolean_t modified;
+
+      SVN_ERR(check_file_modified(&modified, b->db, local_abspath,
+                                  recorded_size, recorded_time, checksum,
+                                  have_props, props_mod, scratch_pool));
+      /* Pin the text-base for modified files. */
+      referenced = modified;
+    }
+
+  *referenced_p = referenced;
+  return SVN_NO_ERROR;
+}
+
+/* Implements svn_wc__db_textbase_hydrate_cb_t. */
+static svn_error_t *
+textbase_hydrate_cb(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)
+{
+  textbase_sync_baton_t *b = baton;
+
+  SVN_ERR(b->hydrate_callback(b->hydrate_baton, repos_root_url,
+                              repos_relpath, revision, contents,
+                              cancel_func, cancel_baton, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__textbase_sync(svn_wc_context_t *wc_ctx,
+                      const char *local_abspath,
+                      svn_boolean_t allow_hydrate,
+                      svn_boolean_t allow_dehydrate,
+                      svn_wc__textbase_hydrate_cb_t hydrate_callback,
+                      void *hydrate_baton,
+                      svn_cancel_func_t cancel_func,
+                      void *cancel_baton,
+                      apr_pool_t *scratch_pool)
+{
+  textbase_sync_baton_t baton = {0};
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  baton.db = wc_ctx->db;
+  baton.hydrate_callback = hydrate_callback;
+  baton.hydrate_baton = hydrate_baton;
+
+  SVN_ERR(svn_wc__db_textbase_walk(wc_ctx->db, local_abspath,
+                                   textbase_walk_cb, &baton,
+                                   cancel_func, cancel_baton,
+                                   scratch_pool));
+
+  SVN_ERR(svn_wc__db_textbase_sync(wc_ctx->db, local_abspath,
+                                   allow_hydrate, allow_dehydrate,
+                                   textbase_hydrate_cb, &baton,
+                                   cancel_func, cancel_baton,
+                                   scratch_pool));
+
+  return SVN_NO_ERROR;
+}

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.h?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.h 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/textbase.h Fri 
Aug 27 13:54:33 2021
@@ -93,6 +93,10 @@ svn_wc__textbase_setaside_wq(const char
                              apr_pool_t *scratch_pool);
 
 /* Prepare to install the text-base contents for file LOCAL_ABSPATH in DB.
+ * If HYDRATED is true, the contents are guaranteed to be kept and available
+ * on disk.  If HYDRATED is false, the contents MAY not be saved on disk,
+ * but the actual state is a subject to the current working copy state and
+ * configuration.
  *
  * For more detail, see the description of 
svn_wc__db_pristine_prepare_install().
  */
@@ -103,6 +107,7 @@ svn_wc__textbase_prepare_install(svn_str
                                  svn_checksum_t **md5_checksum_p,
                                  svn_wc__db_t *db,
                                  const char *local_abspath,
+                                 svn_boolean_t hydrated,
                                  apr_pool_t *result_pool,
                                  apr_pool_t *scratch_pool);
 

Modified: 
subversion/branches/pristines-on-demand/subversion/libsvn_wc/update_editor.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/update_editor.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- 
subversion/branches/pristines-on-demand/subversion/libsvn_wc/update_editor.c 
(original)
+++ 
subversion/branches/pristines-on-demand/subversion/libsvn_wc/update_editor.c 
Fri Aug 27 13:54:33 2021
@@ -3766,6 +3766,21 @@ 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
@@ -3778,6 +3793,7 @@ lazy_open_target(svn_stream_t **stream_p
                                            NULL,
                                            fb->edit_baton->db,
                                            fb->local_abspath,
+                                           hydrated,
                                            result_pool, scratch_pool));
 
   if (fb->shadowed || fb->obstruction_found || fb->edit_obstructed)
@@ -5647,6 +5663,7 @@ svn_wc_add_repos_file4(svn_wc_context_t
                                                &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,

Modified: subversion/branches/pristines-on-demand/subversion/libsvn_wc/upgrade.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pristines-on-demand/subversion/libsvn_wc/upgrade.c?rev=1892650&r1=1892649&r2=1892650&view=diff
==============================================================================
--- subversion/branches/pristines-on-demand/subversion/libsvn_wc/upgrade.c 
(original)
+++ subversion/branches/pristines-on-demand/subversion/libsvn_wc/upgrade.c Fri 
Aug 27 13:54:33 2021
@@ -1077,6 +1077,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,
@@ -1426,6 +1427,16 @@ bump_to_31(void *baton,
 }
 
 static svn_error_t *
+bump_to_32(void *baton,
+           svn_sqlite__db_t *sdb,
+           apr_pool_t *scratch_pool)
+{
+  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_32));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
                         const char *dir_relpath,
                         apr_int64_t wc_id,
@@ -1685,6 +1696,12 @@ svn_wc__upgrade_sdb(int *result_format,
         SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_31, &bb,
                                              scratch_pool));
         *result_format = 31;
+
+      case 31:
+        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_32, &bb,
+                                             scratch_pool));
+        *result_format = 32;
+
         /* FALLTHROUGH  */
       /* ### future bumps go here.  */
 #if 0


Reply via email to