The attached patch is an attempt to close the gap between "transmit text deltas" and commit post-processing, by passing the temporary new text base file paths around. It succeeds in that, at least it looks right and passes the test suite.
The part of this patch that I haven't finished is with back-compat of svn_wc__process_committed_internal(), and what is the difference between its 'queue' parameter and its checksum/recurse/no_unlock/etc. parameters, being values which are alternatively available in the queue. svn_wc__process_committed_internal() is called by svn_wc_process_committed_queue2() which passes the 'queue' param, and also by the deprecated svn_wc_process_committed4() with QUEUE=NULL. I had been assuming that if QUEUE==NULL then all the parameters that are available in the queue (checksum for one) are not available, but that's not how the back-compat wrapper wants to work. I'll need to fix that. I think the right thing to do is to re-write the wrapper (svn_wc_process_committed4()) to construct a new queue with one item and pass that, and stop having the other parameters (checksum etc.) passed as separate parameters. I'll look at that tomorrow. I may already have committed changes that break that back-compat; I'll check. The internal recursion of svn_wc__process_committed_internal() is confusing me: it passes NO_UNLOCK=TRUE to itself when recursing, regardless of what no_unlock value was passed in or what is in the queue item. Yet it passes the received value of KEEP_CHANGELIST. For the NEW_DAV_CACHE and CHECKSUM arguments it passes NULL, which is half explained by them only having meaning in connection with a single node: the same ones cannot be applicable to a child node. Comments? - Julian
### This version of the patch carries the text base path through the client ### 'tempfiles' hash and adds it into the WC commit-items struct. In a commit, pass the temporary paths of the new text base files through from the pre-commit to the post-commit stage, instead of re-deriving them. This completes the data flow of these paths so that they no longer need to be deterministically derivable. * subversion/include/svn_wc.h (svn_wc_queue_committed3): Add a new parameter 'new_text_base_abspath'. ### Also doc that CHECKSUM must be given when applicable, but that should be a separate change. (svn_wc_queue_committed2): Adjust doc string accordingly. * subversion/libsvn_client/commit.c (post_commit_baton, post_process_commit_item, svn_client_commit4): Pass the new text base abspaths down, through the post_commit_baton, to svn_wc_queue_committed3(). * subversion/libsvn_wc/adm_crawler.c (svn_wc_transmit_text_deltas3): ### Brute-force verification of these changes. Not to be committed. * subversion/libsvn_wc/adm_ops.c (committed_queue_item_t): Add a new member 'new_text_base_abspath'. (process_committed_leaf): Take 'new_text_base_abspath' as a parameter instead of deriving it. (svn_wc__process_committed_internal): Find 'new_text_base_abspath' and 'checksum' in the Committed Queue Item and pass them to process_committed_leaf(). Don't take 'checksum' as a param. (svn_wc_queue_committed3): Take 'new_text_base_abspath' as a parameter and store it in the queue. (svn_wc_process_committed_queue2): Adjust the call to svn_wc__process_committed_internal(). * subversion/libsvn_wc/deprecated.c (svn_wc_process_committed4): Adjust the call to svn_wc__process_committed_internal(). (svn_wc_queue_committed2): Implement the old behaviour, looking for a file at a deterministic temporary text base path, and passing that to svn_wc_queue_committed3(). * subversion/libsvn_wc/wc.h (svn_wc__process_committed_internal): Remove the 'checksum' parameter, as it is now passed via the 'queue' parameter. * notes/wc-ng/use-of-tmp-text-base-path Update accordingly. --This line, and those below, will be ignored-- Index: notes/wc-ng/use-of-tmp-text-base-path =================================================================== --- notes/wc-ng/use-of-tmp-text-base-path (revision 928789) +++ notes/wc-ng/use-of-tmp-text-base-path (working copy) @@ -1,5 +1,5 @@ -Call graphs of the use of the WC-1 temporary text base path, as of r927056. +Call graphs of the use of the WC-1 temporary text base path. This is to help us eliminate the use of this path and replace it with a more encapsulated way of referring to the new text base, as part of migration to a @@ -14,46 +14,46 @@ path is obtained, and the extent to whic svn_client_commit4() - |^[T] | [T] Terminates here - wc_to_repos_copy() |^[M] | - | |^ | [M] Multiple files - svn_client__do_commit() | - [N] |^ | [N] Not when caller is - |^ | wc_to_repos_copy() - |^ | - LIBSVN_CLIENT |^ | + |^ |v [T] Terminates here + wc_to_repos_copy() |^[M] |v[M] + | |^ |v [M] Multiple files + svn_client__do_commit() |v + [N] |^ |v [N] Not when caller is + |^ |v wc_to_repos_copy() + |^ |v + LIBSVN_CLIENT |^ |v .......................................................................... - LIBSVN_WC |^ | - |^ | + LIBSVN_WC |^ |v + |^ |v |^ +---+ - |^ | - svn_wc_transmit_text_deltas3() | - [N] |^ | - svn_wc__internal_transmit_text_deltas() | - [N] |^ | - |^ | + |^ |v + svn_wc_transmit_text_deltas3() |v + [N] |^ |v + svn_wc__internal_transmit_text_deltas() |v + [N] |^ |v + |^ |v |^ { svn_wc_process_committed_queue2() } |^ { svn_wc_process_committed4() } - |^ | + |^ |v |^ svn_wc__process_committed_internal() - |^ | + |^ |v |^ process_committed_leaf() - |^ |^ |v - |^ |^ svn_wc__wq_add_postcommit() - |^ |^ *v - |^ |^ *v - |^ |^ *v - |^ |^ WQ:OP_POSTCOMMIT - |^ |^ *v - |^ |^ *v - |^ |^ *v - |^ |^ run_postcommit() - |^ |^ |v - |^ |^ log_do_committed() - |^ |^ |v - |^ |^ install_committed_file() - |^ |^ |v - |^ |^ |v + |^ | |v + |^ | svn_wc__wq_add_postcommit() + |^ | *v + |^ | *v + |^ | *v + |^ | WQ:OP_POSTCOMMIT + |^ | *v + |^ | *v + |^ | *v + |^ | run_postcommit() + |^ | |v + |^ | log_do_committed() + |^ | |v + |^ | install_committed_file() + |^ | |v + |^ | |v svn_wc__text_base_path(tmp=TRUE) svn_wc__sync_text_base() |^ |v |^ [initialization] svn_io_rename() Index: subversion/include/svn_wc.h =================================================================== --- subversion/include/svn_wc.h (revision 928789) +++ subversion/include/svn_wc.h (working copy) @@ -4746,8 +4746,9 @@ svn_wc_committed_queue_create(apr_pool_t * svn_wc_process_committed_queue2(). * * All pointer data passed to this function (@a local_abspath, - * @a wcprop_changes * and @a checksum) should remain valid until the - * queue has been processed by svn_wc_process_committed_queue2(). + * @a wcprop_changes, @a checksum and @a new_text_base_abspath) should + * remain valid until the queue has been processed by + * svn_wc_process_committed_queue2(). * * Record in @a queue that @a local_abspath will need to be bumped * after a commit succeeds. @@ -4765,11 +4766,9 @@ svn_wc_committed_queue_create(apr_pool_t * If @a remove_changelist is @c TRUE, any association with a * changelist will be removed. * - * If @a local_abspath is a file and @a checksum is non-NULL, use @a checksum - * as the checksum for the new text base. Otherwise, calculate the checksum - * if needed. - * ### [JAF] No, it doesn't calculate the checksum, it stores null in wc.db: - * ### see svn_wc__process_committed_internal(). + * If there is a new text base file to be installed, @a new_text_base_abspath + * must be its path and @a checksum must be its MD5 checksum. Otherwise, + * @a new_text_base_abspath and @a checksum must be NULL. * * If @a recurse is TRUE and @a local_abspath is a directory, then bump every * versioned object at or under @a path. This is usually done for @@ -4791,11 +4790,15 @@ svn_wc_queue_committed3(svn_wc_committed const apr_array_header_t *wcprop_changes, svn_boolean_t remove_lock, svn_boolean_t remove_changelist, + const char *new_text_base_abspath, const svn_checksum_t *checksum, apr_pool_t *scratch_pool); /** Same as svn_wc_queue_committed3() except @a path doesn't have to be an - * abspath and @a adm_access is unused. + * abspath, @a adm_access is unused and the new text base file (if there is + * one) is found automatically. + * ### and the docs previously implied @a checksum was optional, but it + * ### doesn't seem so: see svn_wc__process_committed_internal(). * * @since New in 1.6. * Index: subversion/libsvn_client/commit.c =================================================================== --- subversion/libsvn_client/commit.c (revision 928789) +++ subversion/libsvn_client/commit.c (working copy) @@ -941,6 +941,7 @@ struct post_commit_baton svn_wc_context_t *wc_ctx; svn_boolean_t keep_changelists; svn_boolean_t keep_locks; + apr_hash_t *new_text_base_abspaths; apr_hash_t *checksums; }; @@ -984,6 +985,9 @@ post_process_commit_item(void *baton, vo return svn_wc_queue_committed3(btn->queue, item->path, loop_recurse, item->incoming_prop_changes, remove_lock, !btn->keep_changelists, + apr_hash_get(btn->new_text_base_abspaths, + item->path, + APR_HASH_KEY_STRING), apr_hash_get(btn->checksums, item->path, APR_HASH_KEY_STRING), @@ -1098,7 +1102,7 @@ svn_client_commit4(svn_commit_info_t **c apr_array_header_t *rel_targets; apr_hash_t *committables; apr_hash_t *lock_tokens; - apr_hash_t *tempfiles = NULL; + apr_hash_t *new_text_base_abspaths = NULL; apr_hash_t *checksums; apr_array_header_t *commit_items; svn_error_t *cmt_err = SVN_NO_ERROR, *unlock_err = SVN_NO_ERROR; @@ -1256,8 +1260,8 @@ svn_client_commit4(svn_commit_info_t **c /* Perform the commit. */ cmt_err = svn_client__do_commit(base_url, commit_items, editor, edit_baton, - notify_prefix, &tempfiles, &checksums, ctx, - pool); + notify_prefix, &new_text_base_abspaths, + &checksums, ctx, pool); /* Handle a successful commit. */ if ((! cmt_err) @@ -1271,6 +1275,7 @@ svn_client_commit4(svn_commit_info_t **c btn.wc_ctx = ctx->wc_ctx; btn.keep_changelists = keep_changelists; btn.keep_locks = keep_locks; + btn.new_text_base_abspaths = new_text_base_abspaths; btn.checksums = checksums; /* Make a note that our commit is finished. */ @@ -1309,7 +1314,7 @@ svn_client_commit4(svn_commit_info_t **c unlock_err = svn_wc__release_write_lock(ctx->wc_ctx, base_dir, pool); if (! unlock_err) - cleanup_err = remove_tmpfiles(tempfiles, pool); + cleanup_err = remove_tmpfiles(new_text_base_abspaths, pool); } /* As per our promise, if *commit_info_p isn't set, provide a default where Index: subversion/libsvn_wc/adm_crawler.c =================================================================== --- subversion/libsvn_wc/adm_crawler.c (revision 928789) +++ subversion/libsvn_wc/adm_crawler.c (working copy) @@ -1330,10 +1330,36 @@ svn_wc_transmit_text_deltas3(const char apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - return svn_wc__internal_transmit_text_deltas(tempfile, digest, wc_ctx->db, - local_abspath, fulltext, editor, - file_baton, result_pool, - scratch_pool); + const char *tempfile_1; + const char *new_text_base_abspath; + svn_node_kind_t new_text_base_kind; + + SVN_ERR(svn_wc__internal_transmit_text_deltas(&tempfile_1, digest, wc_ctx->db, + local_abspath, fulltext, editor, + file_baton, result_pool, + scratch_pool)); + if (tempfile) + *tempfile = tempfile_1; + + /* For transition, verify that TEMPFILE_1 is non-null iff a file exists + * at the WC-1 deterministic tmp text base path. */ + + SVN_ERR(svn_wc__text_base_path(&new_text_base_abspath, wc_ctx->db, + local_abspath, TRUE, scratch_pool)); + SVN_ERR(svn_io_check_path(new_text_base_abspath, &new_text_base_kind, + scratch_pool)); + if (new_text_base_kind != svn_node_file) + new_text_base_abspath = NULL; + + if (!tempfile_1 != !new_text_base_abspath + || (tempfile_1 && strcmp(tempfile_1, new_text_base_abspath) != 0)) + { + printf("## tempfile_1='%s',\n## but new_t='%s' and kind=%d\n", + tempfile_1, new_text_base_abspath, new_text_base_kind); + abort(); + } + + return SVN_NO_ERROR; } svn_error_t * Index: subversion/libsvn_wc/adm_ops.c =================================================================== --- subversion/libsvn_wc/adm_ops.c (revision 928789) +++ subversion/libsvn_wc/adm_ops.c (working copy) @@ -83,6 +83,7 @@ typedef struct svn_boolean_t recurse; svn_boolean_t no_unlock; svn_boolean_t keep_changelist; + const char *new_text_base_abspath; const svn_checksum_t *checksum; apr_hash_t *new_dav_cache; } committed_queue_item_t; @@ -345,8 +346,8 @@ process_deletion_postcommit(svn_wc__db_t } -/* CHECKSUM is the checksum of the new text base for LOCAL_ABSPATH, and must - * be provided if there is one, else NULL. */ +/* If there is a new text base file for LOCAL_ABSPATH, CHECKSUM must be its + * checksum and NEW_TEXT_BASE_ABSPATH must be its path. */ static svn_error_t * process_committed_leaf(svn_wc__db_t *db, const char *local_abspath, @@ -356,6 +357,7 @@ process_committed_leaf(svn_wc__db_t *db, apr_hash_t *new_dav_cache, svn_boolean_t no_unlock, svn_boolean_t keep_changelist, + const char *new_text_base_abspath, const svn_checksum_t *checksum, apr_pool_t *scratch_pool) { @@ -363,7 +365,6 @@ process_committed_leaf(svn_wc__db_t *db, svn_wc__db_kind_t kind; const svn_checksum_t *copied_checksum; const char *adm_abspath; - const char *tmp_text_base_abspath; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); @@ -431,19 +432,7 @@ process_committed_leaf(svn_wc__db_t *db, SVN_ERR(svn_wc__loggy_delete_lock(db, adm_abspath, local_abspath, scratch_pool)); - /* Set TMP_TEXT_BASE_ABSPATH to the new text base to be installed, if any. */ - { - svn_node_kind_t new_base_kind; - - SVN_ERR(svn_wc__text_base_path(&tmp_text_base_abspath, db, local_abspath, - TRUE, scratch_pool)); - SVN_ERR(svn_io_check_path(tmp_text_base_abspath, &new_base_kind, - scratch_pool)); - if (new_base_kind != svn_node_file) - tmp_text_base_abspath = NULL; - } - - SVN_ERR(svn_wc__wq_add_postcommit(db, local_abspath, tmp_text_base_abspath, + SVN_ERR(svn_wc__wq_add_postcommit(db, local_abspath, new_text_base_abspath, new_revnum, new_date, rev_author, checksum, new_dav_cache, keep_changelist, @@ -463,19 +452,24 @@ svn_wc__process_committed_internal(svn_w apr_hash_t *new_dav_cache, svn_boolean_t no_unlock, svn_boolean_t keep_changelist, - const svn_checksum_t *checksum, const svn_wc_committed_queue_t *queue, apr_pool_t *scratch_pool) { + const committed_queue_item_t *cqi; svn_wc__db_kind_t kind; + cqi = queue ? apr_hash_get(queue->queue, local_abspath, APR_HASH_KEY_STRING) + : NULL; + SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, TRUE, scratch_pool)); SVN_ERR(process_committed_leaf(db, local_abspath, new_revnum, new_date, rev_author, new_dav_cache, no_unlock, keep_changelist, - checksum, scratch_pool)); + cqi ? cqi->new_text_base_abspath : NULL, + cqi ? cqi->checksum : NULL, + scratch_pool)); if (recurse && kind == svn_wc__db_kind_dir) { @@ -533,12 +527,15 @@ svn_wc__process_committed_internal(svn_w rev_author, NULL, TRUE /* no_unlock */, - keep_changelist, NULL, + keep_changelist, queue, iterpool)); SVN_ERR(svn_wc__wq_run(db, this_abspath, NULL, NULL, iterpool)); } else { + cqi = queue ? apr_hash_get(queue->queue, this_abspath, + APR_HASH_KEY_STRING) : NULL; + /* Suppress log creation for deleted entries in a replaced directory. By the time any log we create here is run, those entries will already have been removed (as a result @@ -556,23 +553,15 @@ svn_wc__process_committed_internal(svn_w continue; } - checksum = NULL; - if (queue != NULL) - { - const committed_queue_item_t *cqi - = apr_hash_get(queue->queue, this_abspath, - APR_HASH_KEY_STRING); - - if (cqi != NULL) - checksum = cqi->checksum; - } - SVN_ERR(process_committed_leaf(db, this_abspath, new_revnum, new_date, rev_author, NULL, TRUE /* no_unlock */, keep_changelist, - checksum, iterpool)); + cqi ? cqi->new_text_base_abspath + : NULL, + cqi ? cqi->checksum : NULL, + iterpool)); } } @@ -626,6 +615,7 @@ svn_wc_queue_committed3(svn_wc_committed const apr_array_header_t *wcprop_changes, svn_boolean_t remove_lock, svn_boolean_t remove_changelist, + const char *new_text_base_abspath, const svn_checksum_t *checksum, apr_pool_t *scratch_pool) { @@ -645,6 +635,7 @@ svn_wc_queue_committed3(svn_wc_committed cqi->recurse = recurse; cqi->no_unlock = !remove_lock; cqi->keep_changelist = !remove_changelist; + cqi->new_text_base_abspath = new_text_base_abspath; cqi->checksum = checksum; cqi->new_dav_cache = svn_wc__prop_array_to_hash(wcprop_changes, queue->pool); @@ -725,8 +716,7 @@ svn_wc_process_committed_queue2(svn_wc_c cqi->new_dav_cache, cqi->no_unlock, cqi->keep_changelist, - cqi->checksum, queue, - iterpool)); + queue, iterpool)); SVN_ERR(svn_wc__wq_run(wc_ctx->db, cqi->local_abspath, NULL, NULL, iterpool)); Index: subversion/libsvn_wc/deprecated.c =================================================================== --- subversion/libsvn_wc/deprecated.c (revision 928789) +++ subversion/libsvn_wc/deprecated.c (working copy) @@ -39,6 +39,7 @@ #include "lock.h" #include "props.h" #include "workqueue.h" +#include "adm_files.h" #include "svn_private_config.h" @@ -584,7 +585,7 @@ svn_wc_process_committed4(const char *pa new_revnum, new_date, rev_author, wcprop_changes_hash, !remove_lock, !remove_changelist, - checksum, NULL, pool)); + NULL, pool)); /* Run the log file(s) we just created. */ return svn_error_return(svn_wc__wq_run(db, local_abspath, NULL, NULL, pool)); @@ -3714,10 +3715,26 @@ svn_wc_queue_committed2(svn_wc_committed apr_pool_t *scratch_pool) { const char *local_abspath; + const char *new_text_base_abspath; SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); + + /* Set TMP_TEXT_BASE_ABSPATH to the new text base to be installed, if any. */ + { + svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); + svn_node_kind_t new_base_kind; + + SVN_ERR(svn_wc__text_base_path(&new_text_base_abspath, db, local_abspath, + TRUE, scratch_pool)); + SVN_ERR(svn_io_check_path(new_text_base_abspath, &new_base_kind, + scratch_pool)); + if (new_base_kind != svn_node_file) + new_text_base_abspath = NULL; + } + return svn_wc_queue_committed3(queue, local_abspath, recurse, wcprop_changes, - remove_lock, remove_changelist, checksum, + remove_lock, remove_changelist, + new_text_base_abspath, checksum, scratch_pool); } Index: subversion/libsvn_wc/wc.h =================================================================== --- subversion/libsvn_wc/wc.h (revision 928789) +++ subversion/libsvn_wc/wc.h (working copy) @@ -212,11 +212,6 @@ svn_wc__get_committed_queue_pool(const s * * If @keep_changelist is set, don't remove any changeset assignments * from @a local_abspath; otherwise, clear it of such assignments. - * - * If @a local_abspath is a file and @a checksum is non-NULL, use @a checksum - * as the checksum for the new text base. Otherwise, calculate the checksum - * if needed. - * ### [JAF] No, it doesn't calculate the checksum, it stores null in wc.db. */ svn_error_t * svn_wc__process_committed_internal(svn_wc__db_t *db, @@ -228,7 +223,6 @@ svn_wc__process_committed_internal(svn_w apr_hash_t *new_dav_cache, svn_boolean_t no_unlock, svn_boolean_t keep_changelist, - const svn_checksum_t *checksum, const svn_wc_committed_queue_t *queue, apr_pool_t *scratch_pool);