diff options
Diffstat (limited to 'subversion/libsvn_wc')
51 files changed, 11162 insertions, 8784 deletions
diff --git a/subversion/libsvn_wc/adm_crawler.c b/subversion/libsvn_wc/adm_crawler.c index e5935a2..ebdc75e 100644 --- a/subversion/libsvn_wc/adm_crawler.c +++ b/subversion/libsvn_wc/adm_crawler.c @@ -69,6 +69,8 @@ restore_file(svn_wc__db_t *db, const char *local_abspath, svn_boolean_t use_commit_times, svn_boolean_t mark_resolved_text_conflict, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { svn_skel_t *work_item; @@ -86,12 +88,14 @@ restore_file(svn_wc__db_t *db, /* Run the work item immediately. */ SVN_ERR(svn_wc__wq_run(db, local_abspath, - NULL, NULL, /* ### nice to have cancel_func/baton */ + cancel_func, cancel_baton, scratch_pool)); /* Remove any text conflict */ if (mark_resolved_text_conflict) - SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath, scratch_pool)); + SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); return SVN_NO_ERROR; } @@ -102,6 +106,7 @@ svn_wc_restore(svn_wc_context_t *wc_ctx, svn_boolean_t use_commit_times, apr_pool_t *scratch_pool) { + /* ### If ever revved: Add cancel func. */ svn_wc__db_status_t status; svn_node_kind_t kind; svn_node_kind_t disk_kind; @@ -138,6 +143,7 @@ svn_wc_restore(svn_wc_context_t *wc_ctx, if (kind == svn_node_file || kind == svn_node_symlink) SVN_ERR(restore_file(wc_ctx->db, local_abspath, use_commit_times, FALSE /*mark_resolved_text_conflict*/, + NULL, NULL /* cancel func, baton */, scratch_pool)); else SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool)); @@ -145,7 +151,7 @@ svn_wc_restore(svn_wc_context_t *wc_ctx, return SVN_NO_ERROR; } -/* Try to restore LOCAL_ABSPATH of node type KIND and if successfull, +/* Try to restore LOCAL_ABSPATH of node type KIND and if successful, notify that the node is restored. Use DB for accessing the working copy. If USE_COMMIT_TIMES is set, then set working file's timestamp to last-commit-time. @@ -156,7 +162,10 @@ static svn_error_t * restore_node(svn_wc__db_t *db, const char *local_abspath, svn_node_kind_t kind, + svn_boolean_t mark_resolved_text_conflict, svn_boolean_t use_commit_times, + svn_cancel_func_t cancel_func, + void *cancel_baton, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) @@ -165,7 +174,8 @@ restore_node(svn_wc__db_t *db, { /* Recreate file from text-base; mark any text conflict as resolved */ SVN_ERR(restore_file(db, local_abspath, use_commit_times, - TRUE /*mark_resolved_text_conflict*/, + mark_resolved_text_conflict, + cancel_func, cancel_baton, scratch_pool)); } else if (kind == svn_node_dir) @@ -293,11 +303,11 @@ report_revisions_and_depths(svn_wc__db_t *db, hi != NULL; hi = apr_hash_next(hi)) { - const char *child = svn__apr_hash_index_key(hi); + const char *child = apr_hash_this_key(hi); const char *this_report_relpath; const char *this_abspath; svn_boolean_t this_switched = FALSE; - struct svn_wc__db_base_info_t *ths = svn__apr_hash_index_val(hi); + struct svn_wc__db_base_info_t *ths = apr_hash_this_val(hi); if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); @@ -375,12 +385,13 @@ report_revisions_and_depths(svn_wc__db_t *db, svn_wc__db_status_t wrk_status; svn_node_kind_t wrk_kind; const svn_checksum_t *checksum; + svn_boolean_t conflicted; SVN_ERR(svn_wc__db_read_info(&wrk_status, &wrk_kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &checksum, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, db, this_abspath, iterpool, iterpool)); if ((wrk_status == svn_wc__db_status_normal @@ -399,8 +410,9 @@ report_revisions_and_depths(svn_wc__db_t *db, if (dirent_kind == svn_node_none) { SVN_ERR(restore_node(db, this_abspath, wrk_kind, - use_commit_times, notify_func, - notify_baton, iterpool)); + conflicted, use_commit_times, + cancel_func, cancel_baton, + notify_func, notify_baton, iterpool)); } } } @@ -708,12 +720,13 @@ svn_wc_crawl_revisions5(svn_wc_context_t *wc_ctx, svn_wc__db_status_t wrk_status; svn_node_kind_t wrk_kind; const svn_checksum_t *checksum; + svn_boolean_t conflicted; err = svn_wc__db_read_info(&wrk_status, &wrk_kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, + NULL, &conflicted, NULL, NULL, NULL, NULL, + NULL, NULL, db, local_abspath, scratch_pool, scratch_pool); @@ -733,7 +746,8 @@ svn_wc_crawl_revisions5(svn_wc_context_t *wc_ctx, && (wrk_kind == svn_node_dir || checksum)) { SVN_ERR(restore_node(wc_ctx->db, local_abspath, - wrk_kind, use_commit_times, + wrk_kind, conflicted, use_commit_times, + cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool)); } @@ -877,7 +891,7 @@ read_handler_copy(void *baton, char *buffer, apr_size_t *len) { struct copying_stream_baton *btn = baton; - SVN_ERR(svn_stream_read(btn->source, buffer, len)); + SVN_ERR(svn_stream_read_full(btn->source, buffer, len)); return svn_stream_write(btn->target, buffer, len); } @@ -910,7 +924,8 @@ copying_stream(svn_stream_t *source, baton->target = target; stream = svn_stream_create(baton, pool); - svn_stream_set_read(stream, read_handler_copy); + svn_stream_set_read2(stream, NULL /* only full read support */, + read_handler_copy); svn_stream_set_close(stream, close_handler_copy); return stream; @@ -999,8 +1014,9 @@ svn_wc__internal_transmit_text_deltas(const char **tempfile, svn_checksum_t *verify_checksum; /* calc'd MD5 of BASE_STREAM */ svn_checksum_t *local_md5_checksum; /* calc'd MD5 of LOCAL_STREAM */ svn_checksum_t *local_sha1_checksum; /* calc'd SHA1 of LOCAL_STREAM */ - const char *new_pristine_tmp_abspath; + svn_wc__db_install_data_t *install_data = NULL; svn_error_t *err; + svn_error_t *err2; svn_stream_t *base_stream; /* delta source */ svn_stream_t *local_stream; /* delta target: LOCAL_ABSPATH transl. to NF */ @@ -1037,11 +1053,11 @@ svn_wc__internal_transmit_text_deltas(const char **tempfile, { svn_stream_t *new_pristine_stream; - SVN_ERR(svn_wc__open_writable_base(&new_pristine_stream, - &new_pristine_tmp_abspath, - NULL, &local_sha1_checksum, - db, local_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_pristine_prepare_install(&new_pristine_stream, + &install_data, + &local_sha1_checksum, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); local_stream = copying_stream(local_stream, new_pristine_stream, scratch_pool); } @@ -1097,7 +1113,15 @@ svn_wc__internal_transmit_text_deltas(const char **tempfile, scratch_pool, scratch_pool); /* Close the two streams to force writing the digest */ - err = svn_error_compose_create(err, svn_stream_close(base_stream)); + err2 = svn_stream_close(base_stream); + if (err2) + { + /* Set verify_checksum to NULL if svn_stream_close() returns error + because checksum will be uninitialized in this case. */ + verify_checksum = NULL; + err = svn_error_compose_create(err, err2); + } + err = svn_error_compose_create(err, svn_stream_close(local_stream)); /* If we have an error, it may be caused by a corrupt text base, @@ -1145,7 +1169,7 @@ svn_wc__internal_transmit_text_deltas(const char **tempfile, result_pool); if (new_text_base_sha1_checksum) { - SVN_ERR(svn_wc__db_pristine_install(db, new_pristine_tmp_abspath, + SVN_ERR(svn_wc__db_pristine_install(install_data, local_sha1_checksum, local_md5_checksum, scratch_pool)); diff --git a/subversion/libsvn_wc/adm_files.c b/subversion/libsvn_wc/adm_files.c index 11ad277..67a1357 100644 --- a/subversion/libsvn_wc/adm_files.c +++ b/subversion/libsvn_wc/adm_files.c @@ -117,7 +117,7 @@ svn_wc__adm_child(const char *path, path, adm_dir_name, child, - NULL); + SVN_VA_NULL); } @@ -295,38 +295,6 @@ svn_wc__open_adm_stream(svn_stream_t **stream, } -svn_error_t * -svn_wc__open_writable_base(svn_stream_t **stream, - const char **temp_base_abspath, - svn_checksum_t **md5_checksum, - svn_checksum_t **sha1_checksum, - svn_wc__db_t *db, - const char *wri_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const char *temp_dir_abspath; - SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); - - SVN_ERR(svn_wc__db_pristine_get_tempdir(&temp_dir_abspath, db, wri_abspath, - scratch_pool, scratch_pool)); - SVN_ERR(svn_stream_open_unique(stream, - temp_base_abspath, - temp_dir_abspath, - svn_io_file_del_none, - result_pool, scratch_pool)); - if (md5_checksum) - *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum, - svn_checksum_md5, FALSE, result_pool); - if (sha1_checksum) - *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum, - svn_checksum_sha1, FALSE, result_pool); - - return SVN_NO_ERROR; -} - - - /*** Checking for and creating administrative subdirs. ***/ @@ -464,11 +432,14 @@ svn_wc__internal_ensure_adm(svn_wc__db_t *db, db, local_abspath, scratch_pool, scratch_pool)); else - SVN_ERR(svn_wc__db_scan_base_repos(&db_repos_relpath, - &db_repos_root_url, - &db_repos_uuid, - db, local_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, + &db_repos_relpath, + &db_repos_root_url, + &db_repos_uuid, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); } /* The caller gives us a URL which should match the entry. However, diff --git a/subversion/libsvn_wc/adm_files.h b/subversion/libsvn_wc/adm_files.h index 3712149..8c94f74 100644 --- a/subversion/libsvn_wc/adm_files.h +++ b/subversion/libsvn_wc/adm_files.h @@ -112,30 +112,6 @@ svn_error_t *svn_wc__open_adm_stream(svn_stream_t **stream, apr_pool_t *scratch_pool); -/* Open a writable stream to a temporary (normal or revert) text base, - associated with the versioned file LOCAL_ABSPATH in DB. Set *STREAM to - the opened stream and *TEMP_BASE_ABSPATH to the path to the temporary - file. The temporary file will have an arbitrary unique name, in contrast - to the deterministic name that svn_wc__text_base_deterministic_tmp_path() - returns. - - Arrange that, on stream closure, *MD5_CHECKSUM and *SHA1_CHECKSUM will be - set to the MD-5 and SHA-1 checksums respectively of that file. - MD5_CHECKSUM and/or SHA1_CHECKSUM may be NULL if not wanted. - - Allocate the new stream, path and checksums in RESULT_POOL. - */ -svn_error_t * -svn_wc__open_writable_base(svn_stream_t **stream, - const char **temp_base_abspath, - svn_checksum_t **md5_checksum, - svn_checksum_t **sha1_checksum, - svn_wc__db_t *db, - const char *wri_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - - /* Blow away the admistrative directory associated with DIR_ABSPATH. For single-db this doesn't perform actual work unless the wcroot is passed. */ diff --git a/subversion/libsvn_wc/adm_ops.c b/subversion/libsvn_wc/adm_ops.c index a0f8061..3ccd2e7 100644 --- a/subversion/libsvn_wc/adm_ops.c +++ b/subversion/libsvn_wc/adm_ops.c @@ -36,6 +36,7 @@ #include <apr_time.h> #include <apr_errno.h> +#include "svn_private_config.h" #include "svn_types.h" #include "svn_pools.h" #include "svn_string.h" @@ -53,32 +54,32 @@ #include "conflicts.h" #include "workqueue.h" -#include "svn_private_config.h" -#include "private/svn_subr_private.h" #include "private/svn_dep_compat.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" struct svn_wc_committed_queue_t { /* The pool in which ->queue is allocated. */ apr_pool_t *pool; - /* Mapping (const char *) local_abspath to (committed_queue_item_t *). */ - apr_hash_t *queue; - /* Is any item in the queue marked as 'recursive'? */ - svn_boolean_t have_recursive; + /* Mapping (const char *) wcroot_abspath to svn_wc__db_commit_queue_t * */ + apr_hash_t *wc_queues; }; typedef struct committed_queue_item_t { const char *local_abspath; - svn_boolean_t recurse; - svn_boolean_t no_unlock; - svn_boolean_t keep_changelist; + svn_boolean_t recurse; /* Use legacy recursion */ + svn_boolean_t committed; /* Process the node as committed */ + svn_boolean_t remove_lock; /* Remove existing lock on node */ + svn_boolean_t remove_changelist; /* Remove changelist on node */ - /* The pristine text checksum. */ - const svn_checksum_t *sha1_checksum; + /* The pristine text checksum. NULL if the old value should be kept + and for directories */ + const svn_checksum_t *new_sha1_checksum; - apr_hash_t *new_dav_cache; + apr_hash_t *new_dav_cache; /* New DAV cache for the node */ } committed_queue_item_t; @@ -88,245 +89,6 @@ svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue) return queue->pool; } - - -/*** Finishing updates and commits. ***/ - -/* Queue work items that will finish a commit of the file or directory - * LOCAL_ABSPATH in DB: - * - queue the removal of any "revert-base" props and text files; - * - queue an update of the DB entry for this node - * - * ### The Pristine Store equivalent should be: - * - remember the old BASE_NODE and WORKING_NODE pristine text c'sums; - * - queue an update of the DB entry for this node (incl. updating the - * BASE_NODE c'sum and setting the WORKING_NODE c'sum to NULL); - * - queue deletion of the old pristine texts by the remembered checksums. - * - * CHECKSUM is the checksum of the new text base for LOCAL_ABSPATH, and must - * be provided if there is one, else NULL. - * - * STATUS, KIND, PROP_MODS and OLD_CHECKSUM are the current in-db values of - * the node LOCAL_ABSPATH. - */ -static svn_error_t * -process_committed_leaf(svn_wc__db_t *db, - const char *local_abspath, - svn_boolean_t via_recurse, - svn_wc__db_status_t status, - svn_node_kind_t kind, - svn_boolean_t prop_mods, - const svn_checksum_t *old_checksum, - svn_revnum_t new_revnum, - apr_time_t new_changed_date, - const char *new_changed_author, - apr_hash_t *new_dav_cache, - svn_boolean_t no_unlock, - svn_boolean_t keep_changelist, - const svn_checksum_t *checksum, - apr_pool_t *scratch_pool) -{ - svn_revnum_t new_changed_rev = new_revnum; - svn_skel_t *work_item = NULL; - - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - - { - const char *adm_abspath; - - if (kind == svn_node_dir) - adm_abspath = local_abspath; - else - adm_abspath = svn_dirent_dirname(local_abspath, scratch_pool); - SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool)); - } - - if (status == svn_wc__db_status_deleted) - { - return svn_error_trace( - svn_wc__db_base_remove( - db, local_abspath, - FALSE /* keep_as_working */, - FALSE /* queue_deletes */, - TRUE /* remove_locks */, - (! via_recurse) - ? new_revnum : SVN_INVALID_REVNUM, - NULL, NULL, - scratch_pool)); - } - else if (status == svn_wc__db_status_not_present) - { - /* We are committing the leaf of a copy operation. - We leave the not-present marker to allow pulling in excluded - children of a copy. - - The next update will remove the not-present marker. */ - - return SVN_NO_ERROR; - } - - SVN_ERR_ASSERT(status == svn_wc__db_status_normal - || status == svn_wc__db_status_incomplete - || status == svn_wc__db_status_added); - - if (kind != svn_node_dir) - { - /* If we sent a delta (meaning: post-copy modification), - then this file will appear in the queue and so we should have - its checksum already. */ - if (checksum == NULL) - { - /* It was copied and not modified. We must have a text - base for it. And the node should have a checksum. */ - SVN_ERR_ASSERT(old_checksum != NULL); - - checksum = old_checksum; - - /* Is the node completely unmodified and are we recursing? */ - if (via_recurse && !prop_mods) - { - /* If a copied node itself is not modified, but the op_root of - the copy is committed we have to make sure that changed_rev, - changed_date and changed_author don't change or the working - copy used for committing will show different last modified - information then a clean checkout of exactly the same - revisions. (Issue #3676) */ - - SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, - NULL, &new_changed_rev, - &new_changed_date, - &new_changed_author, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - } - } - - SVN_ERR(svn_wc__wq_build_file_commit(&work_item, - db, local_abspath, - prop_mods, - scratch_pool, scratch_pool)); - } - - /* The new text base will be found in the pristine store by its checksum. */ - SVN_ERR(svn_wc__db_global_commit(db, local_abspath, - new_revnum, new_changed_rev, - new_changed_date, new_changed_author, - checksum, - NULL /* new_children */, - new_dav_cache, - keep_changelist, - no_unlock, - work_item, - scratch_pool)); - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_wc__process_committed_internal(svn_wc__db_t *db, - const char *local_abspath, - svn_boolean_t recurse, - svn_boolean_t top_of_recurse, - svn_revnum_t new_revnum, - apr_time_t new_date, - const char *rev_author, - apr_hash_t *new_dav_cache, - svn_boolean_t no_unlock, - svn_boolean_t keep_changelist, - const svn_checksum_t *sha1_checksum, - const svn_wc_committed_queue_t *queue, - apr_pool_t *scratch_pool) -{ - svn_wc__db_status_t status; - svn_node_kind_t kind; - const svn_checksum_t *old_checksum; - svn_boolean_t prop_mods; - - SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, &old_checksum, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, &prop_mods, NULL, NULL, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - - /* NOTE: be wary of making crazy semantic changes in this function, since - svn_wc_process_committed4() calls this. */ - - SVN_ERR(process_committed_leaf(db, local_abspath, !top_of_recurse, - status, kind, prop_mods, old_checksum, - new_revnum, new_date, rev_author, - new_dav_cache, - no_unlock, keep_changelist, - sha1_checksum, - scratch_pool)); - - /* Only check for recursion on nodes that have children */ - if (kind != svn_node_file - || status == svn_wc__db_status_not_present - || status == svn_wc__db_status_excluded - || status == svn_wc__db_status_server_excluded - /* Node deleted -> then no longer a directory */ - || status == svn_wc__db_status_deleted) - { - return SVN_NO_ERROR; - } - - if (recurse) - { - const apr_array_header_t *children; - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - int i; - - /* Read PATH's entries; this is the absolute path. */ - SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath, - scratch_pool, iterpool)); - - /* Recursively loop over all children. */ - for (i = 0; i < children->nelts; i++) - { - const char *name = APR_ARRAY_IDX(children, i, const char *); - const char *this_abspath; - const committed_queue_item_t *cqi; - - svn_pool_clear(iterpool); - - this_abspath = svn_dirent_join(local_abspath, name, iterpool); - - sha1_checksum = NULL; - cqi = svn_hash_gets(queue->queue, this_abspath); - - if (cqi != NULL) - sha1_checksum = cqi->sha1_checksum; - - /* Recurse. Pass NULL for NEW_DAV_CACHE, because the - ones present in the current call are only applicable to - this one committed item. */ - SVN_ERR(svn_wc__process_committed_internal( - db, this_abspath, - TRUE /* recurse */, - FALSE /* top_of_recurse */, - new_revnum, new_date, - rev_author, - NULL /* new_dav_cache */, - TRUE /* no_unlock */, - keep_changelist, - sha1_checksum, - queue, - iterpool)); - } - - svn_pool_destroy(iterpool); - } - - return SVN_NO_ERROR; -} - - apr_hash_t * svn_wc__prop_array_to_hash(const apr_array_header_t *props, apr_pool_t *result_pool) @@ -357,76 +119,56 @@ svn_wc_committed_queue_create(apr_pool_t *pool) q = apr_palloc(pool, sizeof(*q)); q->pool = pool; - q->queue = apr_hash_make(pool); - q->have_recursive = FALSE; + q->wc_queues = apr_hash_make(pool); return q; } svn_error_t * -svn_wc_queue_committed3(svn_wc_committed_queue_t *queue, +svn_wc_queue_committed4(svn_wc_committed_queue_t *queue, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t recurse, + svn_boolean_t is_committed, const apr_array_header_t *wcprop_changes, svn_boolean_t remove_lock, svn_boolean_t remove_changelist, const svn_checksum_t *sha1_checksum, apr_pool_t *scratch_pool) { - committed_queue_item_t *cqi; + const char *wcroot_abspath; + svn_wc__db_commit_queue_t *db_queue; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - queue->have_recursive |= recurse; - /* Use the same pool as the one QUEUE was allocated in, to prevent lifetime issues. Intermediate operations should use SCRATCH_POOL. */ - /* Add to the array with paths and options */ - cqi = apr_palloc(queue->pool, sizeof(*cqi)); - cqi->local_abspath = local_abspath; - cqi->recurse = recurse; - cqi->no_unlock = !remove_lock; - cqi->keep_changelist = !remove_changelist; - cqi->sha1_checksum = sha1_checksum; - cqi->new_dav_cache = svn_wc__prop_array_to_hash(wcprop_changes, queue->pool); - - svn_hash_sets(queue->queue, local_abspath, cqi); - - return SVN_NO_ERROR; -} - + SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); -/* Return TRUE if any item of QUEUE is a parent of ITEM and will be - processed recursively, return FALSE otherwise. - - The algorithmic complexity of this search implementation is O(queue - length), but it's quite quick. -*/ -static svn_boolean_t -have_recursive_parent(apr_hash_t *queue, - const committed_queue_item_t *item, - apr_pool_t *scratch_pool) -{ - apr_hash_index_t *hi; - const char *local_abspath = item->local_abspath; - - for (hi = apr_hash_first(scratch_pool, queue); hi; hi = apr_hash_next(hi)) + db_queue = svn_hash_gets(queue->wc_queues, wcroot_abspath); + if (! db_queue) { - const committed_queue_item_t *qi = svn__apr_hash_index_val(hi); + wcroot_abspath = apr_pstrdup(queue->pool, wcroot_abspath); - if (qi == item) - continue; + SVN_ERR(svn_wc__db_create_commit_queue(&db_queue, + wc_ctx->db, wcroot_abspath, + queue->pool, scratch_pool)); - if (qi->recurse && svn_dirent_is_child(qi->local_abspath, local_abspath, - NULL)) - return TRUE; + svn_hash_sets(queue->wc_queues, wcroot_abspath, db_queue); } - return FALSE; + return svn_error_trace( + svn_wc__db_commit_queue_add(db_queue, local_abspath, recurse, + is_committed, remove_lock, + remove_changelist, sha1_checksum, + svn_wc__prop_array_to_hash(wcprop_changes, + queue->pool), + queue->pool, scratch_pool)); } @@ -440,75 +182,44 @@ svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue, void *cancel_baton, apr_pool_t *scratch_pool) { - apr_array_header_t *sorted_queue; + apr_array_header_t *wcs; int i; apr_pool_t *iterpool = svn_pool_create(scratch_pool); apr_time_t new_date; - apr_hash_t *run_wqs = apr_hash_make(scratch_pool); - apr_hash_index_t *hi; if (rev_date) SVN_ERR(svn_time_from_cstring(&new_date, rev_date, iterpool)); else new_date = 0; - /* Process the queued items in order of their paths. (The requirement is - * probably just that a directory must be processed before its children.) */ - sorted_queue = svn_sort__hash(queue->queue, svn_sort_compare_items_as_paths, - scratch_pool); - for (i = 0; i < sorted_queue->nelts; i++) + /* Process the wc's in order of their paths. */ + wcs = svn_sort__hash(queue->wc_queues, svn_sort_compare_items_as_paths, + scratch_pool); + for (i = 0; i < wcs->nelts; i++) { const svn_sort__item_t *sort_item - = &APR_ARRAY_IDX(sorted_queue, i, svn_sort__item_t); - const committed_queue_item_t *cqi = sort_item->value; - const char *wcroot_abspath; + = &APR_ARRAY_IDX(wcs, i, svn_sort__item_t); + svn_wc__db_commit_queue_t *db_queue = sort_item->value; svn_pool_clear(iterpool); - /* Skip this item if it is a child of a recursive item, because it has - been (or will be) accounted for when that recursive item was (or - will be) processed. */ - if (queue->have_recursive && have_recursive_parent(queue->queue, cqi, - iterpool)) - continue; - - SVN_ERR(svn_wc__process_committed_internal( - wc_ctx->db, cqi->local_abspath, - cqi->recurse, - TRUE /* top_of_recurse */, - new_revnum, new_date, rev_author, - cqi->new_dav_cache, - cqi->no_unlock, - cqi->keep_changelist, - cqi->sha1_checksum, queue, - iterpool)); - - /* Don't run the wq now, but remember that we must call it for this - working copy */ - SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, - wc_ctx->db, cqi->local_abspath, - iterpool, iterpool)); - - if (! svn_hash_gets(run_wqs, wcroot_abspath)) - { - wcroot_abspath = apr_pstrdup(scratch_pool, wcroot_abspath); - svn_hash_sets(run_wqs, wcroot_abspath, wcroot_abspath); - } + SVN_ERR(svn_wc__db_process_commit_queue(wc_ctx->db, db_queue, + new_revnum, new_date, rev_author, + iterpool)); } /* Make sure nothing happens if this function is called again. */ - apr_hash_clear(queue->queue); + apr_hash_clear(queue->wc_queues); /* Ok; everything is committed now. Now we can start calling callbacks */ - if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); - for (hi = apr_hash_first(scratch_pool, run_wqs); - hi; - hi = apr_hash_next(hi)) + for (i = 0; i < wcs->nelts; i++) { - const char *wcroot_abspath = svn__apr_hash_index_key(hi); + const svn_sort__item_t *sort_item + = &APR_ARRAY_IDX(wcs, i, svn_sort__item_t); + const char *wcroot_abspath = sort_item->key; svn_pool_clear(iterpool); @@ -631,10 +342,12 @@ check_can_add_to_parent(const char **repos_root_url, db, parent_abspath, result_pool, scratch_pool)); else - SVN_ERR(svn_wc__db_scan_base_repos(NULL, - repos_root_url, repos_uuid, - db, parent_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, + repos_root_url, repos_uuid, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + db, parent_abspath, + result_pool, scratch_pool)); } return SVN_NO_ERROR; @@ -887,11 +600,13 @@ svn_wc_add4(svn_wc_context_t *wc_ctx, const char *repos_relpath, *inner_repos_root_url, *inner_repos_uuid; const char *inner_url; - SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath, - &inner_repos_root_url, - &inner_repos_uuid, - db, local_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, &repos_relpath, + &inner_repos_root_url, + &inner_repos_uuid, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + db, local_abspath, + scratch_pool, scratch_pool)); if (strcmp(inner_repos_uuid, repos_uuid) || strcmp(repos_root_url, inner_repos_root_url)) @@ -993,9 +708,10 @@ svn_wc_add4(svn_wc_context_t *wc_ctx, svn_error_t * -svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx, +svn_wc_add_from_disk3(svn_wc_context_t *wc_ctx, const char *local_abspath, const apr_hash_t *props, + svn_boolean_t skip_checks, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) @@ -1014,7 +730,7 @@ svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx, SVN_ERR(svn_wc__canonicalize_props( &new_props, - local_abspath, kind, props, FALSE /* skip_some_checks */, + local_abspath, kind, props, skip_checks, scratch_pool, scratch_pool)); props = new_props; } @@ -1177,10 +893,9 @@ svn_wc_add_lock2(svn_wc_context_t *wc_ctx, SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - /* ### Enable after fixing callers */ - /*SVN_ERR(svn_wc__write_check(wc_ctx->db, + SVN_ERR(svn_wc__write_check(wc_ctx->db, svn_dirent_dirname(local_abspath, scratch_pool), - scratch_pool));*/ + scratch_pool)); db_lock.token = lock->token; db_lock.owner = lock->owner; @@ -1227,16 +942,20 @@ svn_wc_remove_lock2(svn_wc_context_t *wc_ctx, apr_pool_t *scratch_pool) { svn_error_t *err; - const svn_string_t *needs_lock; + svn_skel_t *work_item; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - /* ### Enable after fixing callers */ - /*SVN_ERR(svn_wc__write_check(wc_ctx->db, + SVN_ERR(svn_wc__write_check(wc_ctx->db, svn_dirent_dirname(local_abspath, scratch_pool), - scratch_pool));*/ + scratch_pool)); - err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, scratch_pool); + SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + + err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, work_item, + scratch_pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) @@ -1250,24 +969,9 @@ svn_wc_remove_lock2(svn_wc_context_t *wc_ctx, scratch_pool)); } - /* if svn:needs-lock is present, then make the file read-only. */ - err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath, - SVN_PROP_NEEDS_LOCK, scratch_pool, - scratch_pool); - if (err) - { - if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS) - return svn_error_trace(err); - - svn_error_clear(err); - return SVN_NO_ERROR; /* Node is shadowed and/or deleted, - so we shouldn't apply its lock */ - } - - if (needs_lock) - SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool)); - - return SVN_NO_ERROR; + return svn_error_trace(svn_wc__wq_run(wc_ctx->db, local_abspath, + NULL, NULL /* cancel*/, + scratch_pool)); } @@ -1321,9 +1025,8 @@ get_node_changelist(const char *local_abspath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, b->db, local_abspath, scratch_pool, scratch_pool)); - - if (svn_wc__internal_changelist_match(b->db, local_abspath, b->clhash, - scratch_pool)) + if (!b->clhash + || (changelist && svn_hash_gets(b->clhash, changelist) != NULL)) SVN_ERR(b->callback_func(b->callback_baton, local_abspath, changelist, scratch_pool)); diff --git a/subversion/libsvn_wc/cleanup.c b/subversion/libsvn_wc/cleanup.c index afe7371..491ca93 100644 --- a/subversion/libsvn_wc/cleanup.c +++ b/subversion/libsvn_wc/cleanup.c @@ -81,6 +81,9 @@ status_dummy_callback(void *baton, static svn_error_t * cleanup_internal(svn_wc__db_t *db, const char *dir_abspath, + svn_boolean_t break_locks, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t vacuum_pristines, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) @@ -98,7 +101,7 @@ cleanup_internal(svn_wc__db_t *db, scratch_pool, scratch_pool)); if (lock_abspath) dir_abspath = lock_abspath; - SVN_ERR(svn_wc__db_wclock_obtain(db, dir_abspath, -1, TRUE, scratch_pool)); + SVN_ERR(svn_wc__db_wclock_obtain(db, dir_abspath, -1, break_locks, scratch_pool)); /* Run our changes before the subdirectories. We may not have to recurse if we blow away a subdir. */ @@ -117,7 +120,7 @@ cleanup_internal(svn_wc__db_t *db, svn_wc__check_wcroot() as that function, will just return true once we start sharing databases with externals. */ - if (is_wcroot) + if (is_wcroot && vacuum_pristines) { /* Cleanup the tmp area of the admin subdir, if running the log has not removed it! The logs have been run, so anything left here has no hope @@ -128,17 +131,20 @@ cleanup_internal(svn_wc__db_t *db, SVN_ERR(svn_wc__db_pristine_cleanup(db, dir_abspath, scratch_pool)); } - /* Instead of implementing a separate repair step here, use the standard - status walker's optimized implementation, which performs repairs when - there is a lock. */ - SVN_ERR(svn_wc__internal_walk_status(db, dir_abspath, svn_depth_infinity, - FALSE /* get_all */, - FALSE /* no_ignore */, - FALSE /* ignore_text_mods */, - NULL /* ignore patterns */, - status_dummy_callback, NULL, - cancel_func, cancel_baton, - scratch_pool)); + if (fix_recorded_timestamps) + { + /* Instead of implementing a separate repair step here, use the standard + status walker's optimized implementation, which performs repairs when + there is a lock. */ + SVN_ERR(svn_wc__internal_walk_status(db, dir_abspath, svn_depth_infinity, + FALSE /* get_all */, + FALSE /* no_ignore */, + FALSE /* ignore_text_mods */, + NULL /* ignore patterns */, + status_dummy_callback, NULL, + cancel_func, cancel_baton, + scratch_pool)); + } /* All done, toss the lock */ SVN_ERR(svn_wc__db_wclock_release(db, dir_abspath, scratch_pool)); @@ -146,39 +152,59 @@ cleanup_internal(svn_wc__db_t *db, return SVN_NO_ERROR; } - -/* ### possibly eliminate the WC_CTX parameter? callers really shouldn't - ### be doing anything *but* running a cleanup, and we need a special - ### DB anyway. ... *shrug* ... consider later. */ svn_error_t * -svn_wc_cleanup3(svn_wc_context_t *wc_ctx, +svn_wc_cleanup4(svn_wc_context_t *wc_ctx, const char *local_abspath, + svn_boolean_t break_locks, + svn_boolean_t fix_recorded_timestamps, + svn_boolean_t clear_dav_cache, + svn_boolean_t vacuum_pristines, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, apr_pool_t *scratch_pool) { svn_wc__db_t *db; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + SVN_ERR_ASSERT(wc_ctx != NULL); - /* We need a DB that allows a non-empty work queue (though it *will* - auto-upgrade). We'll handle everything manually. */ - SVN_ERR(svn_wc__db_open(&db, - NULL /* ### config */, FALSE, FALSE, - scratch_pool, scratch_pool)); + if (break_locks) + { + /* We'll handle everything manually. */ - SVN_ERR(cleanup_internal(db, local_abspath, cancel_func, cancel_baton, + /* Close the existing database (if any) to avoid problems with + exclusive database usage */ + SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, local_abspath, + scratch_pool)); + + SVN_ERR(svn_wc__db_open(&db, + NULL /* ### config */, FALSE, FALSE, + scratch_pool, scratch_pool)); + } + else + db = wc_ctx->db; + + SVN_ERR(cleanup_internal(db, local_abspath, + break_locks, + fix_recorded_timestamps, + vacuum_pristines, + cancel_func, cancel_baton, scratch_pool)); /* The DAV cache suffers from flakiness from time to time, and the pre-1.7 prescribed workarounds aren't as user-friendly in WC-NG. */ - SVN_ERR(svn_wc__db_base_clear_dav_cache_recursive(db, local_abspath, - scratch_pool)); + if (clear_dav_cache) + SVN_ERR(svn_wc__db_base_clear_dav_cache_recursive(db, local_abspath, + scratch_pool)); - SVN_ERR(svn_wc__db_vacuum(db, local_abspath, scratch_pool)); + if (vacuum_pristines) + SVN_ERR(svn_wc__db_vacuum(db, local_abspath, scratch_pool)); /* We're done with this DB, so proactively close it. */ - SVN_ERR(svn_wc__db_close(db)); + if (break_locks) + SVN_ERR(svn_wc__db_close(db)); return SVN_NO_ERROR; } diff --git a/subversion/libsvn_wc/conflicts.c b/subversion/libsvn_wc/conflicts.c index f2d6f3e..f04c6de 100644 --- a/subversion/libsvn_wc/conflicts.c +++ b/subversion/libsvn_wc/conflicts.c @@ -477,8 +477,7 @@ svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel, hi; hi = apr_hash_next(hi)) { - svn_skel__prepend_str(apr_pstrdup(result_pool, - svn__apr_hash_index_key(hi)), + svn_skel__prepend_str(apr_pstrdup(result_pool, apr_hash_this_key(hi)), conflict_names, result_pool); } @@ -509,7 +508,7 @@ svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel, } /* A map for svn_wc_conflict_reason_t values. */ -static const svn_token_map_t local_change_map[] = +static const svn_token_map_t reason_map[] = { { "edited", svn_wc_conflict_reason_edited }, { "obstructed", svn_wc_conflict_reason_obstructed }, @@ -523,7 +522,7 @@ static const svn_token_map_t local_change_map[] = { NULL } }; -static const svn_token_map_t incoming_change_map[] = +static const svn_token_map_t action_map[] = { { "edited", svn_wc_conflict_action_edit }, { "added", svn_wc_conflict_action_add }, @@ -536,8 +535,8 @@ svn_error_t * svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel, svn_wc__db_t *db, const char *wri_abspath, - svn_wc_conflict_reason_t local_change, - svn_wc_conflict_action_t incoming_change, + svn_wc_conflict_reason_t reason, + svn_wc_conflict_action_t action, const char *move_src_op_root_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) @@ -550,12 +549,12 @@ svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel, SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */ - SVN_ERR_ASSERT(local_change == svn_wc_conflict_reason_moved_away + SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_moved_away || !move_src_op_root_abspath); /* ### Use proper error? */ tree_conflict = svn_skel__make_empty_list(result_pool); - if (local_change == svn_wc_conflict_reason_moved_away + if (reason == svn_wc_conflict_reason_moved_away && move_src_op_root_abspath) { const char *move_src_op_root_relpath; @@ -569,13 +568,11 @@ svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel, result_pool); } - svn_skel__prepend_str( - svn_token__to_word(incoming_change_map, incoming_change), - tree_conflict, result_pool); + svn_skel__prepend_str(svn_token__to_word(action_map, action), + tree_conflict, result_pool); - svn_skel__prepend_str( - svn_token__to_word(local_change_map, local_change), - tree_conflict, result_pool); + svn_skel__prepend_str(svn_token__to_word(reason_map, reason), + tree_conflict, result_pool); /* Tree conflicts have no marker files */ markers = svn_skel__make_empty_list(result_pool); @@ -931,8 +928,8 @@ svn_wc__conflict_read_prop_conflict(const char **marker_abspath, } svn_error_t * -svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *local_change, - svn_wc_conflict_action_t *incoming_change, +svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *reason, + svn_wc_conflict_action_t *action, const char **move_src_op_root_abspath, svn_wc__db_t *db, const char *wri_abspath, @@ -957,28 +954,28 @@ svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *local_change, c = c->next; /* Skip markers */ { - int value = svn_token__from_mem(local_change_map, c->data, c->len); + int value = svn_token__from_mem(reason_map, c->data, c->len); - if (local_change) + if (reason) { if (value != SVN_TOKEN_UNKNOWN) - *local_change = value; + *reason = value; else - *local_change = svn_wc_conflict_reason_edited; + *reason = svn_wc_conflict_reason_edited; } is_moved_away = (value == svn_wc_conflict_reason_moved_away); } c = c->next; - if (incoming_change) + if (action) { - int value = svn_token__from_mem(incoming_change_map, c->data, c->len); + int value = svn_token__from_mem(action_map, c->data, c->len); if (value != SVN_TOKEN_UNKNOWN) - *incoming_change = value; + *action = value; else - *incoming_change = svn_wc_conflict_action_edit; + *action = svn_wc_conflict_action_edit; } c = c->next; @@ -1050,68 +1047,7 @@ svn_wc__conflict_read_markers(const apr_array_header_t **markers, /* -------------------------------------------------------------------- */ -/* Helper for svn_wc__conflict_create_markers */ -static svn_skel_t * -prop_conflict_skel_new(apr_pool_t *result_pool) -{ - svn_skel_t *operation = svn_skel__make_empty_list(result_pool); - svn_skel_t *result = svn_skel__make_empty_list(result_pool); - - svn_skel__prepend(operation, result); - return result; -} - - -/* Helper for prop_conflict_skel_add */ -static void -prepend_prop_value(const svn_string_t *value, - svn_skel_t *skel, - apr_pool_t *result_pool) -{ - svn_skel_t *value_skel = svn_skel__make_empty_list(result_pool); - - if (value != NULL) - { - const void *dup = apr_pmemdup(result_pool, value->data, value->len); - - svn_skel__prepend(svn_skel__mem_atom(dup, value->len, result_pool), - value_skel); - } - - svn_skel__prepend(value_skel, skel); -} - - -/* Helper for svn_wc__conflict_create_markers */ -static svn_error_t * -prop_conflict_skel_add( - svn_skel_t *skel, - const char *prop_name, - const svn_string_t *original_value, - const svn_string_t *mine_value, - const svn_string_t *incoming_value, - const svn_string_t *incoming_base_value, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_skel_t *prop_skel = svn_skel__make_empty_list(result_pool); - - /* ### check that OPERATION has been filled in. */ - - /* See notes/wc-ng/conflict-storage */ - prepend_prop_value(incoming_base_value, prop_skel, result_pool); - prepend_prop_value(incoming_value, prop_skel, result_pool); - prepend_prop_value(mine_value, prop_skel, result_pool); - prepend_prop_value(original_value, prop_skel, result_pool); - svn_skel__prepend_str(apr_pstrdup(result_pool, prop_name), prop_skel, - result_pool); - svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_skel, result_pool); - /* Now we append PROP_SKEL to the end of the provided conflict SKEL. */ - svn_skel__append(skel, prop_skel); - - return SVN_NO_ERROR; -} svn_error_t * svn_wc__conflict_create_markers(svn_skel_t **work_items, @@ -1141,10 +1077,8 @@ svn_wc__conflict_create_markers(svn_skel_t **work_items, /* Ok, currently we have to do a few things for property conflicts: - Create a marker file - - Create a WQ item that sets the marker name - - Create a WQ item that fills the marker with the expected data - - This can be simplified once we really store conflict_skel in wc.db */ + - Store the name in the conflict_skel + - Create a WQ item that fills the marker with the expected data */ SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); @@ -1176,65 +1110,9 @@ svn_wc__conflict_create_markers(svn_skel_t **work_items, svn_skel__prepend_str(marker_relpath, prop_conflict->children->next, result_pool); } - - /* Store the data in the WQ item in the same format used as 1.7. - Once we store the data in DB it is easier to just read it back - from the workqueue */ - { - svn_skel_t *prop_data; - apr_hash_index_t *hi; - apr_hash_t *old_props; - apr_hash_t *mine_props; - apr_hash_t *their_original_props; - apr_hash_t *their_props; - apr_hash_t *conflicted_props; - - SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL, - &mine_props, - &their_original_props, - &their_props, - &conflicted_props, - db, local_abspath, - conflict_skel, - scratch_pool, - scratch_pool)); - - if (operation == svn_wc_operation_merge) - SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath, - scratch_pool, scratch_pool)); - else - old_props = their_original_props; - - prop_data = prop_conflict_skel_new(result_pool); - - for (hi = apr_hash_first(scratch_pool, conflicted_props); - hi; - hi = apr_hash_next(hi)) - { - const char *propname = svn__apr_hash_index_key(hi); - - SVN_ERR(prop_conflict_skel_add( - prop_data, propname, - old_props - ? svn_hash_gets(old_props, propname) - : NULL, - mine_props - ? svn_hash_gets(mine_props, propname) - : NULL, - their_props - ? svn_hash_gets(their_props, propname) - : NULL, - their_original_props - ? svn_hash_gets(their_original_props, propname) - : NULL, - result_pool, scratch_pool)); - } - - SVN_ERR(svn_wc__wq_build_prej_install(work_items, - db, local_abspath, - prop_data, - scratch_pool, scratch_pool)); - } + SVN_ERR(svn_wc__wq_build_prej_install(work_items, + db, local_abspath, + scratch_pool, scratch_pool)); } return SVN_NO_ERROR; @@ -1264,6 +1142,7 @@ static svn_error_t * generate_propconflict(svn_boolean_t *conflict_remains, svn_wc__db_t *db, const char *local_abspath, + svn_node_kind_t kind, svn_wc_operation_t operation, const svn_wc_conflict_version_t *left_version, const svn_wc_conflict_version_t *right_version, @@ -1274,29 +1153,18 @@ generate_propconflict(svn_boolean_t *conflict_remains, const svn_string_t *incoming_new_val, svn_wc_conflict_resolver_func2_t conflict_func, void *conflict_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { svn_wc_conflict_result_t *result = NULL; svn_wc_conflict_description2_t *cdesc; const char *dirpath = svn_dirent_dirname(local_abspath, scratch_pool); - svn_node_kind_t kind; const svn_string_t *new_value = NULL; - SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, - FALSE /* allow_missing */, - FALSE /* show_deleted */, - FALSE /* show_hidden */, - scratch_pool)); - - if (kind == svn_node_none) - return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, - _("The node '%s' was not found."), - svn_dirent_local_style(local_abspath, - scratch_pool)); - cdesc = svn_wc_conflict_description_create_prop2( local_abspath, - (kind == svn_node_dir) ? svn_node_dir : svn_node_file, + kind, propname, scratch_pool); cdesc->operation = operation; @@ -1313,6 +1181,7 @@ generate_propconflict(svn_boolean_t *conflict_remains, svn_io_file_del_on_pool_cleanup, scratch_pool)); cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); + cdesc->prop_value_working = working_val; } if (incoming_new_val) @@ -1323,7 +1192,11 @@ generate_propconflict(svn_boolean_t *conflict_remains, incoming_new_val->len, svn_io_file_del_on_pool_cleanup, scratch_pool)); - cdesc->their_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); + + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + cdesc->merged_file = svn_dirent_join(dirpath, file_name, scratch_pool); + cdesc->prop_value_incoming_new = incoming_new_val; } if (!base_val && !incoming_old_val) @@ -1332,7 +1205,6 @@ generate_propconflict(svn_boolean_t *conflict_remains, base_file stay NULL as-is. Both agents are attempting to add a new property. */ } - else if ((base_val && !incoming_old_val) || (!base_val && incoming_old_val)) { @@ -1354,7 +1226,6 @@ generate_propconflict(svn_boolean_t *conflict_remains, scratch_pool)); cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); } - else /* base and old are both non-NULL */ { const svn_string_t *conflict_base_val; @@ -1391,6 +1262,9 @@ generate_propconflict(svn_boolean_t *conflict_remains, svn_io_file_del_on_pool_cleanup, scratch_pool)); cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool); + cdesc->prop_value_base = base_val; + cdesc->prop_value_incoming_old = incoming_old_val; + if (working_val && incoming_new_val) { svn_stream_t *mergestream; @@ -1398,17 +1272,22 @@ generate_propconflict(svn_boolean_t *conflict_remains, svn_diff_file_options_t *options = svn_diff_file_options_create(scratch_pool); - SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->merged_file, + SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->prop_reject_abspath, NULL, svn_io_file_del_on_pool_cleanup, scratch_pool, scratch_pool)); SVN_ERR(svn_diff_mem_string_diff3(&diff, conflict_base_val, working_val, incoming_new_val, options, scratch_pool)); - SVN_ERR(svn_diff_mem_string_output_merge2 - (mergestream, diff, conflict_base_val, working_val, + SVN_ERR(svn_diff_mem_string_output_merge3(mergestream, diff, + conflict_base_val, working_val, incoming_new_val, NULL, NULL, NULL, NULL, - svn_diff_conflict_display_modified_latest, scratch_pool)); + svn_diff_conflict_display_modified_latest, + cancel_func, cancel_baton, scratch_pool)); SVN_ERR(svn_stream_close(mergestream)); + + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + cdesc->their_abspath = cdesc->prop_reject_abspath; } } @@ -1427,10 +1306,8 @@ generate_propconflict(svn_boolean_t *conflict_remains, cdesc->reason = svn_wc_conflict_reason_edited; /* Invoke the interactive conflict callback. */ - { - SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool, - scratch_pool)); - } + SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool, + scratch_pool)); if (result == NULL) { *conflict_remains = TRUE; @@ -1476,18 +1353,24 @@ generate_propconflict(svn_boolean_t *conflict_remains, { svn_stringbuf_t *merged_stringbuf; - if (!cdesc->merged_file && !result->merged_file) + if (!cdesc->merged_file + && (!result->merged_file && !result->merged_value)) return svn_error_create (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("Conflict callback violated API:" " returned no merged file")); - SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf, - result->merged_file ? - result->merged_file : - cdesc->merged_file, - scratch_pool)); - new_value = svn_stringbuf__morph_into_string(merged_stringbuf); + if (result->merged_value) + new_value = result->merged_value; + else + { + SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf, + result->merged_file ? + result->merged_file : + cdesc->merged_file, + scratch_pool)); + new_value = svn_stringbuf__morph_into_string(merged_stringbuf); + } *conflict_remains = FALSE; break; } @@ -1513,166 +1396,113 @@ generate_propconflict(svn_boolean_t *conflict_remains, return SVN_NO_ERROR; } -/* Resolve the text conflict on DB/LOCAL_ABSPATH in the manner specified - * by CHOICE. +/* Perform a 3-way merge in which conflicts are expected, showing the + * conflicts in the way specified by STYLE, and using MERGE_OPTIONS. * - * Set *WORK_ITEMS to new work items that will make the on-disk changes - * needed to complete the resolution (but not to mark it as resolved). - * Set *IS_RESOLVED to true if the conflicts are resolved; otherwise - * (which is only if CHOICE is 'postpone') to false. + * The three input files are LEFT_ABSPATH (the base), DETRANSLATED_TARGET + * and RIGHT_ABSPATH. The output is stored in a new temporary file, + * whose name is put into *CHOSEN_ABSPATH. + * + * The output file will be deleted according to DELETE_WHEN. If + * DELETE_WHEN is 'on pool cleanup', it refers to RESULT_POOL. + * + * DB and WRI_ABSPATH are used to choose a directory for the output file. + * + * Allocate *CHOSEN_ABSPATH in RESULT_POOL. Use SCRATCH_POOL for temporary + * allocations. + */ +static svn_error_t * +merge_showing_conflicts(const char **chosen_abspath, + svn_wc__db_t *db, + const char *wri_abspath, + svn_diff_conflict_display_style_t style, + const apr_array_header_t *merge_options, + const char *left_abspath, + const char *detranslated_target, + const char *right_abspath, + svn_io_file_del_t delete_when, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *temp_dir; + svn_stream_t *chosen_stream; + svn_diff_t *diff; + svn_diff_file_options_t *diff3_options; + + diff3_options = svn_diff_file_options_create(scratch_pool); + if (merge_options) + SVN_ERR(svn_diff_file_options_parse(diff3_options, + merge_options, + scratch_pool)); + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db, + wri_abspath, + scratch_pool, scratch_pool)); + /* We need to open the stream in RESULT_POOL because that controls the + * lifetime of the file if DELETE_WHEN is 'on pool cleanup'. (We also + * want to allocate CHOSEN_ABSPATH in RESULT_POOL, but we don't care + * about the stream itself.) */ + SVN_ERR(svn_stream_open_unique(&chosen_stream, chosen_abspath, + temp_dir, delete_when, + result_pool, scratch_pool)); + SVN_ERR(svn_diff_file_diff3_2(&diff, + left_abspath, + detranslated_target, right_abspath, + diff3_options, scratch_pool)); + SVN_ERR(svn_diff_file_output_merge3(chosen_stream, diff, + left_abspath, + detranslated_target, + right_abspath, + NULL, NULL, NULL, NULL, /* markers */ + style, cancel_func, cancel_baton, + scratch_pool)); + SVN_ERR(svn_stream_close(chosen_stream)); + + return SVN_NO_ERROR; +} + +/* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the + * working copy at DB/WRI_ABSPATH. * - * LEFT_ABSPATH, RIGHT_ABSPATH, and DETRANSLATED_TARGET are the - * input files to the 3-way merge that will be performed if CHOICE is - * 'theirs-conflict' or 'mine-conflict'. LEFT_ABSPATH is also the file - * that will be used if CHOICE is 'base', and RIGHT_ABSPATH if CHOICE is - * 'theirs-full'. MERGED_ABSPATH will be used if CHOICE is 'merged'. + * Set *WORK_ITEMS to a new work item that, when run, will delete the + * artifact file; or to NULL if there is no file to delete. * - * DETRANSLATED_TARGET is the detranslated version of 'mine' (see - * detranslate_wc_file() above). MERGE_OPTIONS are passed to the - * diff3 implementation in case a 3-way merge has to be carried out. + * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its + * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND + * may be NULL if not required. */ static svn_error_t * -eval_text_conflict_func_result(svn_skel_t **work_items, - svn_boolean_t *is_resolved, +remove_artifact_file_if_exists(svn_skel_t **work_items, + svn_boolean_t *file_found, svn_wc__db_t *db, - const char *local_abspath, - svn_wc_conflict_choice_t choice, - const apr_array_header_t *merge_options, - const char *left_abspath, - const char *right_abspath, - const char *merged_abspath, - const char *detranslated_target, + const char *wri_abspath, + const char *artifact_file_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - const char *install_from_abspath = NULL; - svn_boolean_t remove_source = FALSE; - *work_items = NULL; - - switch (choice) + if (artifact_file_abspath) { - /* If the callback wants to use one of the fulltexts - to resolve the conflict, so be it.*/ - case svn_wc_conflict_choose_base: - { - install_from_abspath = left_abspath; - *is_resolved = TRUE; - break; - } - case svn_wc_conflict_choose_theirs_full: - { - install_from_abspath = right_abspath; - *is_resolved = TRUE; - break; - } - case svn_wc_conflict_choose_mine_full: - { - install_from_abspath = detranslated_target; - *is_resolved = TRUE; - break; - } - case svn_wc_conflict_choose_theirs_conflict: - case svn_wc_conflict_choose_mine_conflict: - { - const char *chosen_abspath; - const char *temp_dir; - svn_stream_t *chosen_stream; - svn_diff_t *diff; - svn_diff_conflict_display_style_t style; - svn_diff_file_options_t *diff3_options; - - diff3_options = svn_diff_file_options_create(scratch_pool); - - if (merge_options) - SVN_ERR(svn_diff_file_options_parse(diff3_options, - merge_options, - scratch_pool)); - - style = choice == svn_wc_conflict_choose_theirs_conflict - ? svn_diff_conflict_display_latest - : svn_diff_conflict_display_modified; - - SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db, - local_abspath, - scratch_pool, scratch_pool)); - SVN_ERR(svn_stream_open_unique(&chosen_stream, &chosen_abspath, - temp_dir, svn_io_file_del_none, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_diff_file_diff3_2(&diff, - left_abspath, - detranslated_target, right_abspath, - diff3_options, scratch_pool)); - SVN_ERR(svn_diff_file_output_merge2(chosen_stream, diff, - left_abspath, - detranslated_target, - right_abspath, - /* markers ignored */ - NULL, NULL, - NULL, NULL, - style, - scratch_pool)); - SVN_ERR(svn_stream_close(chosen_stream)); - - install_from_abspath = chosen_abspath; - remove_source = TRUE; - *is_resolved = TRUE; - break; - } + svn_node_kind_t node_kind; - /* For the case of 3-way file merging, we don't - really distinguish between these return values; - if the callback claims to have "generally - resolved" the situation, we still interpret - that as "OK, we'll assume the merged version is - good to use". */ - case svn_wc_conflict_choose_merged: - { - install_from_abspath = merged_abspath; - *is_resolved = TRUE; - break; - } - case svn_wc_conflict_choose_postpone: - default: + SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind, + scratch_pool)); + if (node_kind == svn_node_file) { - /* Assume conflict remains. */ - *is_resolved = FALSE; - return SVN_NO_ERROR; + SVN_ERR(svn_wc__wq_build_file_remove(work_items, + db, wri_abspath, + artifact_file_abspath, + result_pool, scratch_pool)); + if (file_found) + *file_found = TRUE; } } - SVN_ERR_ASSERT(install_from_abspath != NULL); - - { - svn_skel_t *work_item; - - SVN_ERR(svn_wc__wq_build_file_install(&work_item, - db, local_abspath, - install_from_abspath, - FALSE /* use_commit_times */, - FALSE /* record_fileinfo */, - result_pool, scratch_pool)); - *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); - - SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath, - result_pool, scratch_pool)); - *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); - - if (remove_source) - { - SVN_ERR(svn_wc__wq_build_file_remove(&work_item, - db, local_abspath, - install_from_abspath, - result_pool, scratch_pool)); - *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); - } - } - return SVN_NO_ERROR; } - /* Create a new file in the same directory as LOCAL_ABSPATH, with the same basename as LOCAL_ABSPATH, with a ".edited" extension, and set *WORK_ITEM to a new work item that will copy and translate from the file @@ -1715,152 +1545,310 @@ save_merge_result(svn_skel_t **work_item, } -/* Call the conflict resolver callback for a text conflict, and resolve - * the conflict if it tells us to do so. - * - * Assume that there is a text conflict on the path DB/LOCAL_ABSPATH. + +/* Resolve the text conflict in CONFLICT, which is currently recorded + * on DB/LOCAL_ABSPATH in the manner specified by CHOICE. * - * Call CONFLICT_FUNC with CONFLICT_BATON to find out whether and how - * it wants to resolve the conflict. Pass it a conflict description - * containing OPERATION, LEFT/RIGHT_ABSPATH, LEFT/RIGHT_VERSION, - * RESULT_TARGET and DETRANSLATED_TARGET. + * Set *WORK_ITEMS to new work items that will make the on-disk changes + * needed to complete the resolution (but not to mark it as resolved). * - * If the callback returns a resolution other than 'postpone', then - * perform that requested resolution and prepare to mark the conflict - * as resolved. + * Set *FOUND_ARTIFACT to true if conflict markers are removed; otherwise + * (which is only if CHOICE is 'postpone') to false. * - * Return *WORK_ITEMS that will do the on-disk work required to complete - * the resolution (but not to mark the conflict as resolved), and set - * *WAS_RESOLVED to true, if it was resolved. Set *WORK_ITEMS to NULL - * and *WAS_RESOLVED to FALSE otherwise. + * CHOICE, MERGED_FILE and SAVE_MERGED are typically values provided by + * the conflict resolver. * - * RESULT_TARGET is the path to the merged file produced by the internal - * or external 3-way merge, which may contain conflict markers, in - * repository normal form. DETRANSLATED_TARGET is the 'mine' version of - * the file, also in RNF. + * MERGE_OPTIONS allows customizing the diff handling when using + * per hunk conflict resolving. */ static svn_error_t * -resolve_text_conflict(svn_skel_t **work_items, - svn_boolean_t *was_resolved, - svn_wc__db_t *db, - const char *local_abspath, - const apr_array_header_t *merge_options, - svn_wc_operation_t operation, - const char *left_abspath, - const char *right_abspath, - const svn_wc_conflict_version_t *left_version, - const svn_wc_conflict_version_t *right_version, - const char *result_target, - const char *detranslated_target, - svn_wc_conflict_resolver_func2_t conflict_func, - void *conflict_baton, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +build_text_conflict_resolve_items(svn_skel_t **work_items, + svn_boolean_t *found_artifact, + svn_wc__db_t *db, + const char *local_abspath, + const svn_skel_t *conflict, + svn_wc_conflict_choice_t choice, + const char *merged_file, + svn_boolean_t save_merged, + const apr_array_header_t *merge_options, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_wc_conflict_result_t *result; + const char *mine_abspath; + const char *their_old_abspath; + const char *their_abspath; svn_skel_t *work_item; - svn_wc_conflict_description2_t *cdesc; - apr_hash_t *props; + const char *install_from_abspath = NULL; + svn_boolean_t remove_source = FALSE; *work_items = NULL; - *was_resolved = FALSE; - - /* Give the conflict resolution callback a chance to clean - up the conflicts before we mark the file 'conflicted' */ - - SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, - scratch_pool, scratch_pool)); - - cdesc = svn_wc_conflict_description_create_text2(local_abspath, - scratch_pool); - cdesc->is_binary = FALSE; - cdesc->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE); - cdesc->base_abspath = left_abspath; - cdesc->their_abspath = right_abspath; - cdesc->my_abspath = detranslated_target; - cdesc->merged_file = result_target; - cdesc->operation = operation; - cdesc->src_left_version = left_version; - cdesc->src_right_version = right_version; - SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool, - scratch_pool)); - if (result == NULL) - return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Conflict callback violated API:" - " returned no results")); + if (found_artifact) + *found_artifact = FALSE; + + SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath, + &their_old_abspath, + &their_abspath, + db, local_abspath, + conflict, + scratch_pool, scratch_pool)); + + if (save_merged) + SVN_ERR(save_merge_result(work_items, + db, local_abspath, + merged_file + ? merged_file + : local_abspath, + result_pool, scratch_pool)); + + if (choice == svn_wc_conflict_choose_postpone) + return SVN_NO_ERROR; - if (result->save_merged) + switch (choice) { - SVN_ERR(save_merge_result(work_items, - db, local_abspath, - /* Look for callback's own - merged-file first: */ - result->merged_file - ? result->merged_file - : result_target, - result_pool, scratch_pool)); + /* If the callback wants to use one of the fulltexts + to resolve the conflict, so be it.*/ + case svn_wc_conflict_choose_base: + { + install_from_abspath = their_old_abspath; + break; + } + case svn_wc_conflict_choose_theirs_full: + { + install_from_abspath = their_abspath; + break; + } + case svn_wc_conflict_choose_mine_full: + { + /* In case of selecting to resolve the conflict choosing the full + own file, allow the text conflict resolution to just take the + existing local file if no merged file was present (case: binary + file conflicts do not generate a locally merge file). + */ + install_from_abspath = mine_abspath + ? mine_abspath + : local_abspath; + break; + } + case svn_wc_conflict_choose_theirs_conflict: + case svn_wc_conflict_choose_mine_conflict: + { + svn_diff_conflict_display_style_t style + = choice == svn_wc_conflict_choose_theirs_conflict + ? svn_diff_conflict_display_latest + : svn_diff_conflict_display_modified; + + if (mine_abspath == NULL) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Conflict on '%s' cannot be resolved to " + "'theirs-conflict' or 'mine-conflict' " + "because a merged version of the file " + "cannot be created."), + svn_dirent_local_style(local_abspath, + scratch_pool)); + + SVN_ERR(merge_showing_conflicts(&install_from_abspath, + db, local_abspath, + style, merge_options, + their_old_abspath, + mine_abspath, + their_abspath, + /* ### why not same as other caller? */ + svn_io_file_del_none, + cancel_func, cancel_baton, + scratch_pool, scratch_pool)); + remove_source = TRUE; + break; + } + + /* For the case of 3-way file merging, we don't + really distinguish between these return values; + if the callback claims to have "generally + resolved" the situation, we still interpret + that as "OK, we'll assume the merged version is + good to use". */ + case svn_wc_conflict_choose_merged: + { + install_from_abspath = merged_file + ? merged_file + : local_abspath; + break; + } + case svn_wc_conflict_choose_postpone: + { + /* Assume conflict remains. */ + return SVN_NO_ERROR; + } + default: + SVN_ERR_ASSERT(choice == svn_wc_conflict_choose_postpone); } - if (result->choice != svn_wc_conflict_choose_postpone) + if (install_from_abspath == NULL) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Conflict on '%s' could not be resolved " + "because the chosen version of the file " + "is not available."), + svn_dirent_local_style(local_abspath, + scratch_pool)); + + /* ### It would be nice if we could somehow pass RECORD_FILEINFO + as true in some easy cases. */ + SVN_ERR(svn_wc__wq_build_file_install(&work_item, + db, local_abspath, + install_from_abspath, + FALSE /* use_commit_times */, + FALSE /* record_fileinfo */, + result_pool, scratch_pool)); + *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); + + if (remove_source) { - SVN_ERR(eval_text_conflict_func_result(&work_item, - was_resolved, - db, local_abspath, - result->choice, - merge_options, - left_abspath, - right_abspath, - /* ### Sure this is an abspath? */ - result->merged_file - ? result->merged_file - : result_target, - detranslated_target, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__wq_build_file_remove(&work_item, + db, local_abspath, + install_from_abspath, + result_pool, scratch_pool)); *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); } - else - *was_resolved = FALSE; + + SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, + db, local_abspath, + their_old_abspath, + result_pool, scratch_pool)); + *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); + + SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, + db, local_abspath, + their_abspath, + result_pool, scratch_pool)); + *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); + + SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact, + db, local_abspath, + mine_abspath, + result_pool, scratch_pool)); + *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); return SVN_NO_ERROR; } +/* Set *DESC to a new description of the text conflict in + * CONFLICT_SKEL. If there is no text conflict in CONFLICT_SKEL, return + * an error. + * + * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION, + * rather than reading them from CONFLICT_SKEL. Use IS_BINARY and + * MIME_TYPE for the corresponding fields of *DESC. + * + * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary + * allocations. */ static svn_error_t * -setup_tree_conflict_desc(svn_wc_conflict_description2_t **desc, - svn_wc__db_t *db, - const char *local_abspath, - svn_wc_operation_t operation, - const svn_wc_conflict_version_t *left_version, - const svn_wc_conflict_version_t *right_version, - svn_wc_conflict_reason_t local_change, - svn_wc_conflict_action_t incoming_change, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +read_text_conflict_desc(svn_wc_conflict_description2_t **desc, + svn_wc__db_t *db, + const char *local_abspath, + const svn_skel_t *conflict_skel, + const char *mime_type, + svn_wc_operation_t operation, + const svn_wc_conflict_version_t *left_version, + const svn_wc_conflict_version_t *right_version, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + *desc = svn_wc_conflict_description_create_text2(local_abspath, result_pool); + (*desc)->mime_type = mime_type; + (*desc)->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE; + (*desc)->operation = operation; + (*desc)->src_left_version = left_version; + (*desc)->src_right_version = right_version; + + SVN_ERR(svn_wc__conflict_read_text_conflict(&(*desc)->my_abspath, + &(*desc)->base_abspath, + &(*desc)->their_abspath, + db, local_abspath, + conflict_skel, + result_pool, scratch_pool)); + (*desc)->merged_file = apr_pstrdup(result_pool, local_abspath); + + return SVN_NO_ERROR; +} + +/* Set *CONFLICT_DESC to a new description of the tree conflict in + * CONFLICT_SKEL. If there is no tree conflict in CONFLICT_SKEL, return + * an error. + * + * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION, + * rather than reading them from CONFLICT_SKEL. + * + * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary + * allocations. */ +static svn_error_t * +read_tree_conflict_desc(svn_wc_conflict_description2_t **desc, + svn_wc__db_t *db, + const char *local_abspath, + svn_node_kind_t node_kind, + const svn_skel_t *conflict_skel, + svn_wc_operation_t operation, + const svn_wc_conflict_version_t *left_version, + const svn_wc_conflict_version_t *right_version, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_node_kind_t tc_kind; + svn_node_kind_t local_kind; + svn_wc_conflict_reason_t reason; + svn_wc_conflict_action_t action; - if (left_version) - tc_kind = left_version->node_kind; - else if (right_version) - tc_kind = right_version->node_kind; + SVN_ERR(svn_wc__conflict_read_tree_conflict( + &reason, &action, NULL, + db, local_abspath, conflict_skel, scratch_pool, scratch_pool)); + + if (reason == svn_wc_conflict_reason_missing) + local_kind = svn_node_none; + else if (reason == svn_wc_conflict_reason_unversioned || + reason == svn_wc_conflict_reason_obstructed) + SVN_ERR(svn_io_check_path(local_abspath, &local_kind, scratch_pool)); + else if (action == svn_wc_conflict_action_delete + && left_version + && (operation == svn_wc_operation_update + ||operation == svn_wc_operation_switch) + && (reason == svn_wc_conflict_reason_deleted + || reason == svn_wc_conflict_reason_moved_away)) + { + /* We have nothing locally to take the kind from */ + local_kind = left_version->node_kind; + } else - tc_kind = svn_node_file; /* Avoid assertion */ + local_kind = node_kind; - *desc = svn_wc_conflict_description_create_tree2(local_abspath, tc_kind, + *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind, operation, left_version, right_version, result_pool); - (*desc)->reason = local_change; - (*desc)->action = incoming_change; + (*desc)->reason = reason; + (*desc)->action = action; return SVN_NO_ERROR; } +/* Forward definition */ +static svn_error_t * +resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, + svn_wc__db_t *db, + const char *local_abspath, + const svn_skel_t *conflict, + svn_wc_conflict_choice_t conflict_choice, + apr_hash_t *resolve_later, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); svn_error_t * svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, const char *local_abspath, + svn_node_kind_t kind, const svn_skel_t *conflict_skel, const apr_array_header_t *merge_options, svn_wc_conflict_resolver_func2_t resolver_func, @@ -1928,7 +1916,7 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, hi; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_key(hi); svn_boolean_t conflict_remains = TRUE; svn_pool_clear(iterpool); @@ -1937,7 +1925,7 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, SVN_ERR(cancel_func(cancel_baton)); SVN_ERR(generate_propconflict(&conflict_remains, - db, local_abspath, + db, local_abspath, kind, operation, left_version, right_version, @@ -1955,6 +1943,7 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, ? svn_hash_gets(their_props, propname) : NULL, resolver_func, resolver_baton, + cancel_func, cancel_baton, iterpool)); if (conflict_remains) @@ -1966,75 +1955,96 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath, scratch_pool)); } + svn_pool_destroy(iterpool); } if (text_conflicted) { - const char *mine_abspath; - const char *their_original_abspath; - const char *their_abspath; svn_skel_t *work_items; svn_boolean_t was_resolved; + svn_wc_conflict_description2_t *desc; + apr_hash_t *props; + svn_wc_conflict_result_t *result; - SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath, - &their_original_abspath, - &their_abspath, - db, local_abspath, - conflict_skel, - scratch_pool, scratch_pool)); - - SVN_ERR(resolve_text_conflict(&work_items, &was_resolved, - db, local_abspath, - merge_options, - operation, - their_original_abspath, their_abspath, - left_version, right_version, - local_abspath, mine_abspath, - resolver_func, resolver_baton, + SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool, scratch_pool)); - if (was_resolved) + SVN_ERR(read_text_conflict_desc(&desc, + db, local_abspath, conflict_skel, + svn_prop_get_value(props, + SVN_PROP_MIME_TYPE), + operation, left_version, right_version, + scratch_pool, scratch_pool)); + + + work_items = NULL; + was_resolved = FALSE; + + /* Give the conflict resolution callback a chance to clean + up the conflicts before we mark the file 'conflicted' */ + + SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool, + scratch_pool)); + if (result == NULL) + return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Conflict callback violated API:" + " returned no results")); + + SVN_ERR(build_text_conflict_resolve_items(&work_items, &was_resolved, + db, local_abspath, + conflict_skel, result->choice, + result->merged_file, + result->save_merged, + merge_options, + cancel_func, cancel_baton, + scratch_pool, scratch_pool)); + + if (result->choice != svn_wc_conflict_choose_postpone) { - if (work_items) - { - SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_items, - scratch_pool)); - SVN_ERR(svn_wc__wq_run(db, local_abspath, - cancel_func, cancel_baton, - scratch_pool)); - } - SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath, - scratch_pool)); + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, + TRUE, FALSE, FALSE, + work_items, scratch_pool)); + SVN_ERR(svn_wc__wq_run(db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); } } if (tree_conflicted) { - svn_wc_conflict_reason_t local_change; - svn_wc_conflict_action_t incoming_change; svn_wc_conflict_result_t *result; svn_wc_conflict_description2_t *desc; + svn_boolean_t resolved; + svn_node_kind_t node_kind; - SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, - &incoming_change, - NULL, - db, local_abspath, - conflict_skel, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_kind(&node_kind, db, local_abspath, TRUE, + TRUE, FALSE, scratch_pool)); - SVN_ERR(setup_tree_conflict_desc(&desc, - db, local_abspath, - operation, left_version, right_version, - local_change, incoming_change, - scratch_pool, scratch_pool)); + SVN_ERR(read_tree_conflict_desc(&desc, + db, local_abspath, node_kind, + conflict_skel, + operation, left_version, right_version, + scratch_pool, scratch_pool)); /* Tell the resolver func about this conflict. */ SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool, scratch_pool)); - /* Ignore the result. We cannot apply it here since this code runs - * during an update or merge operation. Tree conflicts are always - * postponed and resolved after the operation has completed. */ + if (result == NULL) + return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("Conflict callback violated API:" + " returned no results")); + + /* Pass retry hash to avoid erroring out on cases where update + can continue safely. ### Need notify handling */ + if (result->choice != svn_wc_conflict_choose_postpone) + SVN_ERR(resolve_tree_conflict_on_node(&resolved, + db, local_abspath, conflict_skel, + result->choice, + apr_hash_make(scratch_pool), + NULL, NULL, /* ### notify */ + cancel_func, cancel_baton, + scratch_pool)); } return SVN_NO_ERROR; @@ -2042,7 +2052,8 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, /* Read all property conflicts contained in CONFLICT_SKEL into * individual conflict descriptions, and append those descriptions - * to the CONFLICTS array. + * to the CONFLICTS array. If there is no property conflict in + * CONFLICT_SKEL, return an error. * * If NOT create_tempfiles, always create a legacy property conflict * descriptor. @@ -2053,27 +2064,36 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary * allocations. */ static svn_error_t * -read_prop_conflicts(apr_array_header_t *conflicts, - svn_wc__db_t *db, - const char *local_abspath, - svn_skel_t *conflict_skel, - svn_boolean_t create_tempfiles, - svn_node_kind_t node_kind, - svn_wc_operation_t operation, - const svn_wc_conflict_version_t *left_version, - const svn_wc_conflict_version_t *right_version, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +read_prop_conflict_descs(apr_array_header_t *conflicts, + svn_wc__db_t *db, + const char *local_abspath, + svn_skel_t *conflict_skel, + svn_boolean_t create_tempfiles, + svn_node_kind_t node_kind, + svn_wc_operation_t operation, + const svn_wc_conflict_version_t *left_version, + const svn_wc_conflict_version_t *right_version, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - const char *prop_reject_file; + const char *prop_reject_abspath; + apr_hash_t *base_props; apr_hash_t *my_props; apr_hash_t *their_old_props; apr_hash_t *their_props; apr_hash_t *conflicted_props; apr_hash_index_t *hi; apr_pool_t *iterpool; + svn_boolean_t prop_conflicted; - SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file, + SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted, + NULL, db, local_abspath, conflict_skel, + scratch_pool, scratch_pool)); + + if (!prop_conflicted) + return SVN_NO_ERROR; + + SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath, &my_props, &their_old_props, &their_props, @@ -2082,7 +2102,9 @@ read_prop_conflicts(apr_array_header_t *conflicts, conflict_skel, scratch_pool, scratch_pool)); - if ((! create_tempfiles) || apr_hash_count(conflicted_props) == 0) + prop_reject_abspath = apr_pstrdup(result_pool, prop_reject_abspath); + + if (apr_hash_count(conflicted_props) == 0) { /* Legacy prop conflict with only a .reject file. */ svn_wc_conflict_description2_t *desc; @@ -2091,26 +2113,31 @@ read_prop_conflicts(apr_array_header_t *conflicts, node_kind, "", result_pool); - /* ### This should be changed. The prej file should be stored - * ### separately from the other files. We need to rev the - * ### conflict description struct for this. */ - desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file); + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */ + desc->their_abspath = desc->prop_reject_abspath; desc->operation = operation; desc->src_left_version = left_version; desc->src_right_version = right_version; - APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc; + APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc; return SVN_NO_ERROR; } + if (operation == svn_wc_operation_merge) + SVN_ERR(svn_wc__db_read_pristine_props(&base_props, db, local_abspath, + result_pool, scratch_pool)); + else + base_props = NULL; iterpool = svn_pool_create(scratch_pool); for (hi = apr_hash_first(scratch_pool, conflicted_props); hi; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_key(hi); svn_string_t *old_value; svn_string_t *my_value; svn_string_t *their_value; @@ -2118,10 +2145,10 @@ read_prop_conflicts(apr_array_header_t *conflicts, svn_pool_clear(iterpool); - desc = svn_wc_conflict_description_create_prop2(local_abspath, - node_kind, - propname, - result_pool); + desc = svn_wc_conflict_description_create_prop2(local_abspath, + node_kind, + propname, + result_pool); desc->operation = operation; desc->src_left_version = left_version; @@ -2149,26 +2176,30 @@ read_prop_conflicts(apr_array_header_t *conflicts, else desc->reason = svn_wc_conflict_reason_edited; - /* ### This should be changed. The prej file should be stored - * ### separately from the other files. We need to rev the - * ### conflict description struct for this. */ - desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file); + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */ + desc->their_abspath = desc->prop_reject_abspath; + + desc->prop_value_base = base_props ? svn_hash_gets(base_props, propname) + : desc->prop_value_incoming_old; - /* ### This should be changed. The conflict description for - * ### props should contain these values as svn_string_t, - * ### rather than in temporary files. We need to rev the - * ### conflict description struct for this. */ if (my_value) { svn_stream_t *s; apr_size_t len; - SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL, - svn_io_file_del_on_pool_cleanup, - result_pool, iterpool)); - len = my_value->len; - SVN_ERR(svn_stream_write(s, my_value->data, &len)); - SVN_ERR(svn_stream_close(s)); + if (create_tempfiles) + { + SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, iterpool)); + len = my_value->len; + SVN_ERR(svn_stream_write(s, my_value->data, &len)); + SVN_ERR(svn_stream_close(s)); + } + + desc->prop_value_working = svn_string_dup(my_value, result_pool); } if (their_value) @@ -2176,15 +2207,19 @@ read_prop_conflicts(apr_array_header_t *conflicts, svn_stream_t *s; apr_size_t len; - /* ### Currently, their_abspath is used for the prop reject file. - * ### Put their value into merged instead... - * ### We need to rev the conflict description struct to fix this. */ - SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL, - svn_io_file_del_on_pool_cleanup, - result_pool, iterpool)); - len = their_value->len; - SVN_ERR(svn_stream_write(s, their_value->data, &len)); - SVN_ERR(svn_stream_close(s)); + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + if (create_tempfiles) + { + SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, iterpool)); + len = their_value->len; + SVN_ERR(svn_stream_write(s, their_value->data, &len)); + SVN_ERR(svn_stream_close(s)); + } + + desc->prop_value_incoming_new = svn_string_dup(their_value, result_pool); } if (old_value) @@ -2192,15 +2227,20 @@ read_prop_conflicts(apr_array_header_t *conflicts, svn_stream_t *s; apr_size_t len; - SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL, - svn_io_file_del_on_pool_cleanup, - result_pool, iterpool)); - len = old_value->len; - SVN_ERR(svn_stream_write(s, old_value->data, &len)); - SVN_ERR(svn_stream_close(s)); + if (create_tempfiles) + { + SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, iterpool)); + len = old_value->len; + SVN_ERR(svn_stream_write(s, old_value->data, &len)); + SVN_ERR(svn_stream_close(s)); + } + + desc->prop_value_incoming_old = svn_string_dup(old_value, result_pool); } - APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc; + APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc; } svn_pool_destroy(iterpool); @@ -2209,13 +2249,15 @@ read_prop_conflicts(apr_array_header_t *conflicts, svn_error_t * svn_wc__read_conflicts(const apr_array_header_t **conflicts, + svn_skel_t **conflict_skel, svn_wc__db_t *db, const char *local_abspath, svn_boolean_t create_tempfiles, + svn_boolean_t only_tree_conflict, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_skel_t *conflict_skel; + svn_skel_t *the_conflict_skel; apr_array_header_t *cflcts; svn_boolean_t prop_conflicted; svn_boolean_t text_conflicted; @@ -2224,82 +2266,71 @@ svn_wc__read_conflicts(const apr_array_header_t **conflicts, const apr_array_header_t *locations; const svn_wc_conflict_version_t *left_version = NULL; const svn_wc_conflict_version_t *right_version = NULL; - - SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db, local_abspath, - scratch_pool, scratch_pool)); + svn_node_kind_t node_kind; + apr_hash_t *props; if (!conflict_skel) + conflict_skel = &the_conflict_skel; + + SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props, + db, local_abspath, + (conflict_skel == &the_conflict_skel) + ? scratch_pool + : result_pool, + scratch_pool)); + + if (!*conflict_skel) { /* Some callers expect not NULL */ *conflicts = apr_array_make(result_pool, 0, - sizeof(svn_wc_conflict_description2_t*));; + sizeof(svn_wc_conflict_description2_t *)); return SVN_NO_ERROR; } SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted, &prop_conflicted, &tree_conflicted, - db, local_abspath, conflict_skel, + db, local_abspath, *conflict_skel, result_pool, scratch_pool)); cflcts = apr_array_make(result_pool, 4, - sizeof(svn_wc_conflict_description2_t*)); + sizeof(svn_wc_conflict_description2_t *)); if (locations && locations->nelts > 0) left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *); if (locations && locations->nelts > 1) right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *); - if (prop_conflicted) + if (prop_conflicted && !only_tree_conflict) { - svn_node_kind_t node_kind - = left_version ? left_version->node_kind : svn_node_unknown; - - SVN_ERR(read_prop_conflicts(cflcts, db, local_abspath, conflict_skel, - create_tempfiles, node_kind, - operation, left_version, right_version, - result_pool, scratch_pool)); + SVN_ERR(read_prop_conflict_descs(cflcts, + db, local_abspath, *conflict_skel, + create_tempfiles, node_kind, + operation, left_version, right_version, + result_pool, scratch_pool)); } - if (text_conflicted) + if (text_conflicted && !only_tree_conflict) { svn_wc_conflict_description2_t *desc; - desc = svn_wc_conflict_description_create_text2(local_abspath, - result_pool); - desc->operation = operation; - desc->src_left_version = left_version; - desc->src_right_version = right_version; - - SVN_ERR(svn_wc__conflict_read_text_conflict(&desc->my_abspath, - &desc->base_abspath, - &desc->their_abspath, - db, local_abspath, - conflict_skel, - result_pool, scratch_pool)); - - desc->merged_file = apr_pstrdup(result_pool, local_abspath); - - APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t*) = desc; + SVN_ERR(read_text_conflict_desc(&desc, + db, local_abspath, *conflict_skel, + svn_prop_get_value(props, + SVN_PROP_MIME_TYPE), + operation, left_version, right_version, + result_pool, scratch_pool)); + APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t *) = desc; } if (tree_conflicted) { - svn_wc_conflict_reason_t local_change; - svn_wc_conflict_action_t incoming_change; svn_wc_conflict_description2_t *desc; - SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, - &incoming_change, - NULL, - db, local_abspath, - conflict_skel, - scratch_pool, scratch_pool)); - - SVN_ERR(setup_tree_conflict_desc(&desc, - db, local_abspath, - operation, left_version, right_version, - local_change, incoming_change, - result_pool, scratch_pool)); + SVN_ERR(read_tree_conflict_desc(&desc, + db, local_abspath, node_kind, + *conflict_skel, + operation, left_version, right_version, + result_pool, scratch_pool)); APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc; } @@ -2311,206 +2342,6 @@ svn_wc__read_conflicts(const apr_array_header_t **conflicts, /*** Resolving a conflict automatically ***/ -/* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the - * working copy at DB/WRI_ABSPATH. - * - * Set *WORK_ITEMS to a new work item that, when run, will delete the - * artifact file; or to NULL if there is no file to delete. - * - * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its - * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND - * may be NULL if not required. - */ -static svn_error_t * -remove_artifact_file_if_exists(svn_skel_t **work_items, - svn_boolean_t *file_found, - svn_wc__db_t *db, - const char *wri_abspath, - const char *artifact_file_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - *work_items = NULL; - if (artifact_file_abspath) - { - svn_node_kind_t node_kind; - - SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind, - scratch_pool)); - if (node_kind == svn_node_file) - { - SVN_ERR(svn_wc__wq_build_file_remove(work_items, - db, wri_abspath, - artifact_file_abspath, - result_pool, scratch_pool)); - if (file_found) - *file_found = TRUE; - } - } - - return SVN_NO_ERROR; -} - -/* - * Resolve the text conflict found in DB/LOCAL_ABSPATH according - * to CONFLICT_CHOICE. - * - * It is not an error if there is no text conflict. If a text conflict - * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE. - * - * Note: When there are no conflict markers to remove there is no existing - * text conflict; just a database containing old information, which we should - * remove to avoid checking all the time. Resolving a text conflict by - * removing all the marker files is a fully supported scenario since - * Subversion 1.0. - */ -static svn_error_t * -resolve_text_conflict_on_node(svn_boolean_t *did_resolve, - svn_wc__db_t *db, - const char *local_abspath, - svn_wc_conflict_choice_t conflict_choice, - const char *merged_file, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - const char *conflict_old = NULL; - const char *conflict_new = NULL; - const char *conflict_working = NULL; - const char *auto_resolve_src; - svn_skel_t *work_item; - svn_skel_t *work_items = NULL; - svn_skel_t *conflicts; - svn_wc_operation_t operation; - svn_boolean_t text_conflicted; - - *did_resolve = FALSE; - - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, - scratch_pool, scratch_pool)); - if (!conflicts) - return SVN_NO_ERROR; - - SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, &text_conflicted, - NULL, NULL, db, local_abspath, conflicts, - scratch_pool, scratch_pool)); - if (!text_conflicted) - return SVN_NO_ERROR; - - SVN_ERR(svn_wc__conflict_read_text_conflict(&conflict_working, - &conflict_old, - &conflict_new, - db, local_abspath, conflicts, - scratch_pool, scratch_pool)); - - /* Handle automatic conflict resolution before the temporary files are - * deleted, if necessary. */ - switch (conflict_choice) - { - case svn_wc_conflict_choose_base: - auto_resolve_src = conflict_old; - break; - case svn_wc_conflict_choose_mine_full: - auto_resolve_src = conflict_working; - break; - case svn_wc_conflict_choose_theirs_full: - auto_resolve_src = conflict_new; - break; - case svn_wc_conflict_choose_merged: - auto_resolve_src = merged_file; - break; - case svn_wc_conflict_choose_theirs_conflict: - case svn_wc_conflict_choose_mine_conflict: - { - if (conflict_old && conflict_working && conflict_new) - { - const char *temp_dir; - svn_stream_t *tmp_stream = NULL; - svn_diff_t *diff; - svn_diff_conflict_display_style_t style = - conflict_choice == svn_wc_conflict_choose_theirs_conflict - ? svn_diff_conflict_display_latest - : svn_diff_conflict_display_modified; - - SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db, - local_abspath, - scratch_pool, - scratch_pool)); - SVN_ERR(svn_stream_open_unique(&tmp_stream, - &auto_resolve_src, - temp_dir, - svn_io_file_del_on_pool_cleanup, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_diff_file_diff3_2(&diff, - conflict_old, - conflict_working, - conflict_new, - svn_diff_file_options_create( - scratch_pool), - scratch_pool)); - SVN_ERR(svn_diff_file_output_merge2(tmp_stream, diff, - conflict_old, - conflict_working, - conflict_new, - /* markers ignored */ - NULL, NULL, NULL, NULL, - style, - scratch_pool)); - SVN_ERR(svn_stream_close(tmp_stream)); - } - else - auto_resolve_src = NULL; - break; - } - default: - return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, - _("Invalid 'conflict_result' argument")); - } - - if (auto_resolve_src) - { - SVN_ERR(svn_wc__wq_build_file_copy_translated( - &work_item, db, local_abspath, - auto_resolve_src, local_abspath, scratch_pool, scratch_pool)); - work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); - - SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, - local_abspath, - scratch_pool, scratch_pool)); - work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); - } - - /* Legacy behavior: Only report text conflicts as resolved when at least - one conflict marker file exists. - - If not the UI shows the conflict as already resolved - (and in this case we just remove the in-db conflict) */ - - SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve, - db, local_abspath, conflict_old, - scratch_pool, scratch_pool)); - work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); - - SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve, - db, local_abspath, conflict_new, - scratch_pool, scratch_pool)); - work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); - - SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve, - db, local_abspath, conflict_working, - scratch_pool, scratch_pool)); - work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); - - SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, - TRUE, FALSE, FALSE, - work_items, scratch_pool)); - SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, - scratch_pool)); - - return SVN_NO_ERROR; -} - /* * Resolve the property conflicts found in DB/LOCAL_ABSPATH according * to CONFLICT_CHOICE. @@ -2550,9 +2381,11 @@ static svn_error_t * resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, svn_wc__db_t *db, const char *local_abspath, + svn_skel_t *conflicts, const char *conflicted_propname, svn_wc_conflict_choice_t conflict_choice, const char *merged_file, + const svn_string_t *merged_value, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) @@ -2565,18 +2398,13 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, apr_hash_t *old_props; apr_hash_t *resolve_from = NULL; svn_skel_t *work_items = NULL; - svn_skel_t *conflicts; svn_wc_operation_t operation; svn_boolean_t prop_conflicted; + apr_hash_t *actual_props; + svn_boolean_t resolved_all, resolved_all_prop; *did_resolve = FALSE; - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, - scratch_pool, scratch_pool)); - - if (!conflicts) - return SVN_NO_ERROR; - SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted, NULL, db, local_abspath, conflicts, scratch_pool, scratch_pool)); @@ -2589,12 +2417,35 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, db, local_abspath, conflicts, scratch_pool, scratch_pool)); + if (!conflicted_props) + { + /* We have a pre 1.8 property conflict. Just mark it resolved */ + + SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve, + db, local_abspath, prop_reject_file, + scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE, + work_items, scratch_pool)); + SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, + scratch_pool)); + return SVN_NO_ERROR; + } + + if (conflicted_propname[0] != '\0' + && !svn_hash_gets(conflicted_props, conflicted_propname)) + { + return SVN_NO_ERROR; /* This property is not conflicted! */ + } + if (operation == svn_wc_operation_merge) SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath, scratch_pool, scratch_pool)); else old_props = their_old_props; + SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, + scratch_pool, scratch_pool)); + /* We currently handle *_conflict as *_full as this argument is currently always applied for all conflicts on a node at the same time. Giving an error would break some tests that assumed that this would just @@ -2620,21 +2471,23 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, resolve_from = their_props; break; case svn_wc_conflict_choose_merged: - if (merged_file && conflicted_propname[0] != '\0') + if ((merged_file || merged_value) && conflicted_propname[0] != '\0') { - apr_hash_t *actual_props; - svn_stream_t *stream; - svn_string_t *merged_propval; + resolve_from = apr_hash_copy(scratch_pool, actual_props); - SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, - scratch_pool, scratch_pool)); - resolve_from = actual_props; + if (!merged_value) + { + svn_stream_t *stream; + svn_string_t *merged_propval; - SVN_ERR(svn_stream_open_readonly(&stream, merged_file, - scratch_pool, scratch_pool)); - SVN_ERR(svn_string_from_stream(&merged_propval, stream, - scratch_pool, scratch_pool)); - svn_hash_sets(resolve_from, conflicted_propname, merged_propval); + SVN_ERR(svn_stream_open_readonly(&stream, merged_file, + scratch_pool, scratch_pool)); + SVN_ERR(svn_string_from_stream(&merged_propval, stream, + scratch_pool, scratch_pool)); + + merged_value = merged_propval; + } + svn_hash_sets(resolve_from, conflicted_propname, merged_value); } else resolve_from = NULL; @@ -2644,47 +2497,97 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, _("Invalid 'conflict_result' argument")); } - if (conflicted_props && apr_hash_count(conflicted_props) && resolve_from) + + if (resolve_from) { apr_hash_index_t *hi; - apr_hash_t *actual_props; + apr_hash_t *apply_on_props; - SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, - scratch_pool, scratch_pool)); + if (conflicted_propname[0] == '\0') + { + /* Apply to all conflicted properties */ + apply_on_props = conflicted_props; + } + else + { + /* Apply to a single property */ + apply_on_props = apr_hash_make(scratch_pool); + svn_hash_sets(apply_on_props, conflicted_propname, ""); + } - for (hi = apr_hash_first(scratch_pool, conflicted_props); + /* Apply the selected changes */ + for (hi = apr_hash_first(scratch_pool, apply_on_props); hi; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_key(hi); svn_string_t *new_value = NULL; new_value = svn_hash_gets(resolve_from, propname); svn_hash_sets(actual_props, propname, new_value); } - SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props, - FALSE, NULL, NULL, - scratch_pool)); } + /*else the user accepted the properties as-is */ - /* Legacy behavior: Only report property conflicts as resolved when the - property reject file exists + /* This function handles conflicted_propname "" as resolving + all property conflicts... Just what we need here */ + SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts, + db, local_abspath, + FALSE, conflicted_propname, + FALSE, + scratch_pool, scratch_pool)); - If not the UI shows the conflict as already resolved - (and in this case we just remove the in-db conflict) */ + if (!resolved_all) + { + /* Are there still property conflicts left? (or only...) */ + SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, &prop_conflicted, + NULL, db, local_abspath, conflicts, + scratch_pool, scratch_pool)); - { - svn_skel_t *work_item; + resolved_all_prop = (! prop_conflicted); + } + else + { + resolved_all_prop = TRUE; + conflicts = NULL; + } - SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve, - db, local_abspath, prop_reject_file, - scratch_pool, scratch_pool)); - work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); - } + if (resolved_all_prop) + { + /* Legacy behavior: Only report property conflicts as resolved when the + property reject file exists + + If not the UI shows the conflict as already resolved + (and in this case we just remove the in-db conflict) */ + SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve, + db, local_abspath, + prop_reject_file, + scratch_pool, scratch_pool)); + } + else + { + /* Create a new prej file, based on the remaining conflicts */ + SVN_ERR(svn_wc__wq_build_prej_install(&work_items, + db, local_abspath, + scratch_pool, scratch_pool)); + *did_resolve = TRUE; /* We resolved a property conflict */ + } + + /* This installs the updated conflict skel */ + SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props, + FALSE, conflicts, work_items, + scratch_pool)); + + if (resolved_all) + { + /* Remove the whole conflict. Should probably be integrated + into the op_set_props() call */ + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, + FALSE, TRUE, FALSE, + NULL, scratch_pool)); + } - SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE, - work_items, scratch_pool)); SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, scratch_pool)); @@ -2699,12 +2602,18 @@ resolve_prop_conflict_on_node(svn_boolean_t *did_resolve, * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE. * * It is not an error if there is no tree conflict. + * + * If the conflict can't be resolved yet because another tree conflict is + * blocking a storage location, store the tree conflict in the RESOLVE_LATER + * hash. */ static svn_error_t * resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, svn_wc__db_t *db, const char *local_abspath, + const svn_skel_t *conflicts, svn_wc_conflict_choice_t conflict_choice, + apr_hash_t *resolve_later, svn_wc_notify_func2_t notify_func, void *notify_baton, svn_cancel_func_t cancel_func, @@ -2713,24 +2622,20 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, { svn_wc_conflict_reason_t reason; svn_wc_conflict_action_t action; - svn_skel_t *conflicts; svn_wc_operation_t operation; svn_boolean_t tree_conflicted; + const char *src_op_root_abspath; *did_resolve = FALSE; - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, - scratch_pool, scratch_pool)); - if (!conflicts) - return SVN_NO_ERROR; - SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, &tree_conflicted, db, local_abspath, conflicts, scratch_pool, scratch_pool)); if (!tree_conflicted) return SVN_NO_ERROR; - SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL, + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, + &src_op_root_abspath, db, local_abspath, conflicts, scratch_pool, scratch_pool)); @@ -2738,6 +2643,7 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, if (operation == svn_wc_operation_update || operation == svn_wc_operation_switch) { + svn_error_t *err; if (reason == svn_wc_conflict_reason_deleted || reason == svn_wc_conflict_reason_replaced) { @@ -2745,21 +2651,88 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, { /* Break moves for any children moved out of this directory, * and leave this directory deleted. */ - SVN_ERR(svn_wc__db_resolve_break_moved_away_children( - db, local_abspath, notify_func, notify_baton, - scratch_pool)); + + if (action != svn_wc_conflict_action_delete) + { + SVN_ERR(svn_wc__db_op_break_moved_away( + db, local_abspath, src_op_root_abspath, TRUE, + notify_func, notify_baton, + scratch_pool)); + *did_resolve = TRUE; + return SVN_NO_ERROR; /* Marked resolved by function*/ + } + /* else # The move is/moves are already broken */ + + *did_resolve = TRUE; } else if (conflict_choice == svn_wc_conflict_choose_mine_conflict) { - /* Raised moved-away conflicts on any children moved out of - * this directory, and leave this directory deleted. + svn_skel_t *new_conflicts; + + /* Raise moved-away conflicts on any children moved out of + * this directory, and leave this directory as-is. + * * The newly conflicted moved-away children will be updated * if they are resolved with 'mine_conflict' as well. */ - SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away( + err = svn_wc__db_op_raise_moved_away( db, local_abspath, notify_func, notify_baton, - scratch_pool)); - *did_resolve = TRUE; + scratch_pool); + + if (err) + { + const char *dup_abspath; + + if (!resolve_later + || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE) + return svn_error_trace(err); + + svn_error_clear(err); + dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later), + local_abspath); + + svn_hash_sets(resolve_later, dup_abspath, dup_abspath); + + return SVN_NO_ERROR; /* Retry after other conflicts */ + } + + /* We might now have a moved-away on *this* path, let's + try to resolve that directly if that is the case */ + SVN_ERR(svn_wc__db_read_conflict(&new_conflicts, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + if (new_conflicts) + SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL, + &tree_conflicted, + db, local_abspath, + new_conflicts, + scratch_pool, + scratch_pool)); + + if (!new_conflicts || !tree_conflicted) + { + /* TC is marked resolved by calling + svn_wc__db_resolve_delete_raise_moved_away */ + *did_resolve = TRUE; + return SVN_NO_ERROR; + } + + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, + &src_op_root_abspath, + db, local_abspath, + new_conflicts, + scratch_pool, + scratch_pool)); + + if (reason != svn_wc_conflict_reason_moved_away) + { + *did_resolve = TRUE; + return SVN_NO_ERROR; /* We fixed one, but... */ + } + + conflicts = new_conflicts; + /* Fall through in moved_away handling */ } else return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, @@ -2770,8 +2743,9 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, svn_dirent_local_style(local_abspath, scratch_pool)); } - else if (reason == svn_wc_conflict_reason_moved_away - && action == svn_wc_conflict_action_edit) + + if (reason == svn_wc_conflict_reason_moved_away + && action == svn_wc_conflict_action_edit) { /* After updates, we can resolve local moved-away * vs. any incoming change, either by updating the @@ -2779,12 +2753,31 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, * move (theirs-conflict). */ if (conflict_choice == svn_wc_conflict_choose_mine_conflict) { - SVN_ERR(svn_wc__db_update_moved_away_conflict_victim( - db, local_abspath, - notify_func, notify_baton, + err = svn_wc__db_update_moved_away_conflict_victim( + db, local_abspath, src_op_root_abspath, + operation, action, reason, cancel_func, cancel_baton, - scratch_pool)); - *did_resolve = TRUE; + notify_func, notify_baton, + scratch_pool); + + if (err) + { + const char *dup_abspath; + + if (!resolve_later + || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE) + return svn_error_trace(err); + + svn_error_clear(err); + dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later), + local_abspath); + + svn_hash_sets(resolve_later, dup_abspath, dup_abspath); + + return SVN_NO_ERROR; /* Retry after other conflicts */ + } + else + *did_resolve = TRUE; } else if (conflict_choice == svn_wc_conflict_choose_merged) { @@ -2792,14 +2785,12 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, * working copy state instead of updating the move. * Else the move would be left in an invalid state. */ - /* ### This breaks the move but leaves the conflict - ### involving the move until - ### svn_wc__db_op_mark_resolved. */ - SVN_ERR(svn_wc__db_resolve_break_moved_away(db, local_abspath, - notify_func, - notify_baton, - scratch_pool)); + SVN_ERR(svn_wc__db_op_break_moved_away(db, local_abspath, + src_op_root_abspath, TRUE, + notify_func, notify_baton, + scratch_pool)); *did_resolve = TRUE; + return SVN_NO_ERROR; /* Conflict is marked resolved */ } else return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, @@ -2810,22 +2801,57 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, svn_dirent_local_style(local_abspath, scratch_pool)); } + else if (reason == svn_wc_conflict_reason_moved_away + && action != svn_wc_conflict_action_edit) + { + /* action added is impossible, because that would imply that + something was added, but before that already moved... + (which would imply a replace) */ + SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete + || action == svn_wc_conflict_action_replace); + + if (conflict_choice == svn_wc_conflict_choose_merged) + { + /* Whatever was moved is removed at its original location by the + update. That must also remove the recording of the move, so + we don't have to do anything here. */ + + *did_resolve = TRUE; + } + else if (conflict_choice == svn_wc_conflict_choose_mine_conflict) + { + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, + NULL, + _("Tree conflict can only be " + "resolved to 'working' state; " + "'%s' is no longer moved"), + svn_dirent_local_style(local_abspath, + scratch_pool)); + } + } } - if (! *did_resolve && conflict_choice != svn_wc_conflict_choose_merged) + if (! *did_resolve) { - /* For other tree conflicts, there is no way to pick - * theirs-full or mine-full, etc. Throw an error if the - * user expects us to be smarter than we really are. */ - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, - NULL, - _("Tree conflict can only be " - "resolved to 'working' state; " - "'%s' not resolved"), - svn_dirent_local_style(local_abspath, - scratch_pool)); + if (conflict_choice != svn_wc_conflict_choose_merged) + { + /* For other tree conflicts, there is no way to pick + * theirs-full or mine-full, etc. Throw an error if the + * user expects us to be smarter than we really are. */ + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, + NULL, + _("Tree conflict can only be " + "resolved to 'working' state; " + "'%s' not resolved"), + svn_dirent_local_style(local_abspath, + scratch_pool)); + } + else + *did_resolve = TRUE; } + SVN_ERR_ASSERT(*did_resolve); + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE, NULL, scratch_pool)); SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, @@ -2836,16 +2862,33 @@ resolve_tree_conflict_on_node(svn_boolean_t *did_resolve, svn_error_t * svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db, const char *local_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { - svn_boolean_t ignored_result; + svn_skel_t *work_items; + svn_skel_t *conflict; - return svn_error_trace(resolve_text_conflict_on_node( - &ignored_result, - db, local_abspath, - svn_wc_conflict_choose_merged, NULL, - NULL, NULL, - scratch_pool)); + SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + if (!conflict) + return SVN_NO_ERROR; + + SVN_ERR(build_text_conflict_resolve_items(&work_items, NULL, + db, local_abspath, conflict, + svn_wc_conflict_choose_merged, + NULL, FALSE, NULL, + cancel_func, cancel_baton, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, TRUE, FALSE, FALSE, + work_items, scratch_pool)); + + return svn_error_trace(svn_wc__wq_run(db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); } svn_error_t * @@ -2854,11 +2897,20 @@ svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db, apr_pool_t *scratch_pool) { svn_boolean_t ignored_result; + svn_skel_t *conflicts; + + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + if (!conflicts) + return SVN_NO_ERROR; return svn_error_trace(resolve_prop_conflict_on_node( &ignored_result, - db, local_abspath, "", - svn_wc_conflict_choose_merged, NULL, + db, local_abspath, conflicts, "", + svn_wc_conflict_choose_merged, + NULL, NULL, NULL, NULL, scratch_pool)); } @@ -2878,8 +2930,39 @@ struct conflict_status_walker_baton void *cancel_baton; svn_wc_notify_func2_t notify_func; void *notify_baton; + svn_boolean_t resolved_one; + apr_hash_t *resolve_later; }; +/* Implements svn_wc_notify_func2_t to collect new conflicts caused by + resolving a tree conflict. */ +static void +tree_conflict_collector(void *baton, + const svn_wc_notify_t *notify, + apr_pool_t *pool) +{ + struct conflict_status_walker_baton *cswb = baton; + + if (cswb->notify_func) + cswb->notify_func(cswb->notify_baton, notify, pool); + + if (cswb->resolve_later + && (notify->action == svn_wc_notify_tree_conflict + || notify->prop_state == svn_wc_notify_state_conflicted + || notify->content_state == svn_wc_notify_state_conflicted)) + { + if (!svn_hash_gets(cswb->resolve_later, notify->path)) + { + const char *dup_path; + + dup_path = apr_pstrdup(apr_hash_pool_get(cswb->resolve_later), + notify->path); + + svn_hash_sets(cswb->resolve_later, dup_path, dup_path); + } + } +} + /* Implements svn_wc_status4_t to walk all conflicts to resolve. */ static svn_error_t * @@ -2895,13 +2978,17 @@ conflict_status_walker(void *baton, apr_pool_t *iterpool; int i; svn_boolean_t resolved = FALSE; + svn_skel_t *conflict; if (!status->conflicted) return SVN_NO_ERROR; iterpool = svn_pool_create(scratch_pool); - SVN_ERR(svn_wc__read_conflicts(&conflicts, db, local_abspath, TRUE, + SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict, + db, local_abspath, + (cswb->conflict_func != NULL) /* tmp files */, + FALSE /* only tree conflicts */, scratch_pool, iterpool)); for (i = 0; i < conflicts->nelts; i++) @@ -2909,11 +2996,15 @@ conflict_status_walker(void *baton, const svn_wc_conflict_description2_t *cd; svn_boolean_t did_resolve; svn_wc_conflict_choice_t my_choice = cswb->conflict_choice; - const char *merged_file = NULL; + svn_wc_conflict_result_t *result = NULL; + svn_skel_t *work_items; cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *); - if ((cd->kind == svn_wc_conflict_kind_property && !cswb->resolve_prop) + if ((cd->kind == svn_wc_conflict_kind_property + && (!cswb->resolve_prop + || (*cswb->resolve_prop != '\0' + && strcmp(cswb->resolve_prop, cd->property_name) != 0))) || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text) || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree)) { @@ -2924,8 +3015,6 @@ conflict_status_walker(void *baton, if (my_choice == svn_wc_conflict_choose_unspecified) { - svn_wc_conflict_result_t *result; - if (!cswb->conflict_func) return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("No conflict-callback and no " @@ -2935,8 +3024,6 @@ conflict_status_walker(void *baton, iterpool, iterpool)); my_choice = result->choice; - merged_file = result->merged_file; - /* ### Bug: ignores result->save_merged */ } @@ -2946,54 +3033,57 @@ conflict_status_walker(void *baton, switch (cd->kind) { case svn_wc_conflict_kind_tree: - if (!cswb->resolve_tree) - break; SVN_ERR(resolve_tree_conflict_on_node(&did_resolve, db, - local_abspath, + local_abspath, conflict, my_choice, - cswb->notify_func, - cswb->notify_baton, + cswb->resolve_later, + tree_conflict_collector, + cswb, cswb->cancel_func, cswb->cancel_baton, iterpool)); - resolved = TRUE; + if (did_resolve) + resolved = TRUE; break; case svn_wc_conflict_kind_text: - if (!cswb->resolve_text) - break; - - SVN_ERR(resolve_text_conflict_on_node(&did_resolve, - db, - local_abspath, - my_choice, - merged_file, - cswb->cancel_func, - cswb->cancel_baton, - iterpool)); - - if (did_resolve) - resolved = TRUE; + SVN_ERR(build_text_conflict_resolve_items( + &work_items, + &resolved, + db, local_abspath, conflict, + my_choice, + result ? result->merged_file + : NULL, + result ? result->save_merged + : FALSE, + NULL /* merge_options */, + cswb->cancel_func, + cswb->cancel_baton, + iterpool, iterpool)); + + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, + TRUE, FALSE, FALSE, + work_items, iterpool)); + SVN_ERR(svn_wc__wq_run(db, local_abspath, + cswb->cancel_func, cswb->cancel_baton, + iterpool)); break; case svn_wc_conflict_kind_property: - if (!cswb->resolve_prop) - break; - - if (*cswb->resolve_prop != '\0' && - strcmp(cswb->resolve_prop, cd->property_name) != 0) - { - break; /* This is not the property we want to resolve. */ - } - SVN_ERR(resolve_prop_conflict_on_node(&did_resolve, db, local_abspath, + conflict, cd->property_name, my_choice, - merged_file, + result + ? result->merged_file + : NULL, + result + ? result->merged_value + : NULL, cswb->cancel_func, cswb->cancel_baton, iterpool)); @@ -3016,6 +3106,9 @@ conflict_status_walker(void *baton, iterpool), iterpool); + if (resolved) + cswb->resolved_one = TRUE; + svn_pool_destroy(iterpool); return SVN_NO_ERROR; @@ -3040,13 +3133,8 @@ svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx, svn_node_kind_t kind; svn_boolean_t conflicted; struct conflict_status_walker_baton cswb; - - /* ### the underlying code does NOT support resolving individual - ### properties. bail out if the caller tries it. */ - if (resolve_prop != NULL && *resolve_prop != '\0') - return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, - U_("Resolving a single property is not (yet) " - "supported.")); + apr_pool_t *iterpool = NULL; + svn_error_t *err; /* ### Just a versioned check? */ /* Conflicted is set to allow invoking on actual only nodes */ @@ -3079,6 +3167,11 @@ svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx, cswb.notify_func = notify_func; cswb.notify_baton = notify_baton; + cswb.resolved_one = FALSE; + cswb.resolve_later = (depth != svn_depth_empty) + ? apr_hash_make(scratch_pool) + : NULL; + if (notify_func) notify_func(notify_baton, svn_wc_create_notify(local_abspath, @@ -3086,16 +3179,103 @@ svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx, scratch_pool), scratch_pool); - SVN_ERR(svn_wc_walk_status(wc_ctx, - local_abspath, - depth, - FALSE /* get_all */, - FALSE /* no_ignore */, - TRUE /* ignore_text_mods */, - NULL /* ignore_patterns */, - conflict_status_walker, &cswb, - cancel_func, cancel_baton, - scratch_pool)); + err = svn_wc_walk_status(wc_ctx, + local_abspath, + depth, + FALSE /* get_all */, + FALSE /* no_ignore */, + TRUE /* ignore_text_mods */, + NULL /* ignore_patterns */, + conflict_status_walker, &cswb, + cancel_func, cancel_baton, + scratch_pool); + + /* If we got new tree conflicts (or delayed conflicts) during the initial + walk, we now walk them one by one as closure. */ + while (!err && cswb.resolve_later && apr_hash_count(cswb.resolve_later)) + { + apr_hash_index_t *hi; + svn_wc_status3_t *status = NULL; + const char *tc_abspath = NULL; + + if (iterpool) + svn_pool_clear(iterpool); + else + iterpool = svn_pool_create(scratch_pool); + + hi = apr_hash_first(scratch_pool, cswb.resolve_later); + cswb.resolve_later = apr_hash_make(scratch_pool); + cswb.resolved_one = FALSE; + + for (; hi && !err; hi = apr_hash_next(hi)) + { + const char *relpath; + svn_pool_clear(iterpool); + + tc_abspath = apr_hash_this_key(hi); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + + relpath = svn_dirent_skip_ancestor(local_abspath, + tc_abspath); + + if (!relpath + || (depth >= svn_depth_empty + && depth < svn_depth_infinity + && strchr(relpath, '/'))) + { + continue; + } + + SVN_ERR(svn_wc_status3(&status, wc_ctx, tc_abspath, + iterpool, iterpool)); + + if (depth == svn_depth_files + && status->kind == svn_node_dir) + continue; + + err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath, + status, scratch_pool)); + } + + /* None of the remaining conflicts got resolved, and non did provide + an error... + + We can fix that if we disable the 'resolve_later' option... + */ + if (!cswb.resolved_one && !err && tc_abspath + && apr_hash_count(cswb.resolve_later)) + { + /* Run the last resolve operation again. We still have status + and tc_abspath for that one. */ + + cswb.resolve_later = NULL; /* Produce proper error! */ + + /* Recreate the error */ + err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath, + status, scratch_pool)); + + SVN_ERR_ASSERT(err != NULL); + + err = svn_error_createf( + SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err, + _("Unable to resolve pending conflict on '%s'"), + svn_dirent_local_style(tc_abspath, scratch_pool)); + break; + } + } + + if (iterpool) + svn_pool_destroy(iterpool); + + if (err && err->apr_err != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE) + err = svn_error_createf( + SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err, + _("Unable to resolve conflicts on '%s'"), + svn_dirent_local_style(local_abspath, scratch_pool)); + + SVN_ERR(err); if (notify_func) notify_func(notify_baton, @@ -3139,7 +3319,7 @@ svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice, { svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result)); result->choice = choice; - result->merged_file = merged_file; + result->merged_file = apr_pstrdup(pool, merged_file); result->save_merged = FALSE; /* If we add more fields to svn_wc_conflict_result_t, add them here. */ diff --git a/subversion/libsvn_wc/conflicts.h b/subversion/libsvn_wc/conflicts.h index 839e8a0..0a9324b 100644 --- a/subversion/libsvn_wc/conflicts.h +++ b/subversion/libsvn_wc/conflicts.h @@ -419,6 +419,7 @@ svn_wc__conflict_create_markers(svn_skel_t **work_item, svn_error_t * svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, const char *local_abspath, + svn_node_kind_t kind, const svn_skel_t *conflict_skel, const apr_array_header_t *merge_options, svn_wc_conflict_resolver_func2_t resolver_func, @@ -432,6 +433,8 @@ svn_wc__conflict_invoke_resolver(svn_wc__db_t *db, svn_error_t * svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db, const char *local_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool); /* Mark as resolved any prop conflicts on the node at DB/LOCAL_ABSPATH. */ diff --git a/subversion/libsvn_wc/copy.c b/subversion/libsvn_wc/copy.c index 1e7d7cf..30a0db5 100644 --- a/subversion/libsvn_wc/copy.c +++ b/subversion/libsvn_wc/copy.c @@ -42,6 +42,7 @@ #include "svn_private_config.h" #include "private/svn_wc_private.h" +/* #define RECORD_MIXED_MOVE */ /*** Code. ***/ @@ -50,7 +51,14 @@ TMPDIR_ABSPATH and return the absolute path of the copy in *DST_ABSPATH. Return the node kind of SRC_ABSPATH in *KIND. If SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate - that no copy was made. */ + that no copy was made. + + If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. + RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of + SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. + + These values will be used to avoid unneeded work. + */ static svn_error_t * copy_to_tmpdir(svn_skel_t **work_item, svn_node_kind_t *kind, @@ -60,6 +68,9 @@ copy_to_tmpdir(svn_skel_t **work_item, const char *tmpdir_abspath, svn_boolean_t file_copy, svn_boolean_t unversioned, + const svn_io_dirent2_t *dirent, + svn_filesize_t recorded_size, + apr_time_t recorded_time, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, @@ -74,8 +85,14 @@ copy_to_tmpdir(svn_skel_t **work_item, *work_item = NULL; - SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, - scratch_pool)); + if (dirent) + { + *kind = dirent->kind; + is_special = dirent->special; + } + else + SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, + scratch_pool)); if (*kind == svn_node_none) { return SVN_NO_ERROR; @@ -104,9 +121,21 @@ copy_to_tmpdir(svn_skel_t **work_item, the timestamp might match, than to examine the destination later as the destination timestamp will never match. */ - SVN_ERR(svn_wc__internal_file_modified_p(&modified, - db, src_abspath, - FALSE, scratch_pool)); + + if (dirent + && dirent->kind == svn_node_file + && recorded_size != SVN_INVALID_FILESIZE + && recorded_size == dirent->filesize + && recorded_time == dirent->mtime) + { + modified = FALSE; /* Recorded matches on-disk. Easy out */ + } + else + { + SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, src_abspath, + FALSE, scratch_pool)); + } + if (!modified) { /* Why create a temp copy if we can just reinstall from pristine? */ @@ -117,6 +146,15 @@ copy_to_tmpdir(svn_skel_t **work_item, return SVN_NO_ERROR; } } + else if (*kind == svn_node_dir && !file_copy) + { + /* Just build a new direcory from the workqueue */ + SVN_ERR(svn_wc__wq_build_dir_install(work_item, + db, dst_abspath, + result_pool, scratch_pool)); + + return SVN_NO_ERROR; + } /* Set DST_TMP_ABSPATH to a temporary unique path. If *KIND is file, leave a file there and then overwrite it; otherwise leave no node on disk at @@ -172,7 +210,14 @@ copy_to_tmpdir(svn_skel_t **work_item, versioned file itself. This also works for versioned symlinks that are stored in the db as - svn_node_file with svn:special set. */ + svn_node_file with svn:special set. + + If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. + RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of + SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. + + These values will be used to avoid unneeded work. +*/ static svn_error_t * copy_versioned_file(svn_wc__db_t *db, const char *src_abspath, @@ -182,6 +227,9 @@ copy_versioned_file(svn_wc__db_t *db, svn_boolean_t metadata_only, svn_boolean_t conflicted, svn_boolean_t is_move, + const svn_io_dirent2_t *dirent, + svn_filesize_t recorded_size, + apr_time_t recorded_time, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -210,8 +258,9 @@ copy_versioned_file(svn_wc__db_t *db, svn_error_t *err; /* Is there a text conflict at the source path? */ - SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, + db, src_abspath, + scratch_pool, scratch_pool)); err = svn_wc__conflict_read_text_conflict(&conflict_working, NULL, NULL, db, src_abspath, conflict, @@ -248,6 +297,7 @@ copy_versioned_file(svn_wc__db_t *db, dst_abspath, tmpdir_abspath, TRUE /* file_copy */, handle_as_unversioned /* unversioned */, + dirent, recorded_size, recorded_time, cancel_func, cancel_baton, scratch_pool, scratch_pool)); } @@ -265,10 +315,6 @@ copy_versioned_file(svn_wc__db_t *db, scratch_pool); notify->kind = svn_node_file; - /* When we notify that we performed a copy, make sure we already did */ - if (work_items != NULL) - SVN_ERR(svn_wc__wq_run(db, dst_abspath, - cancel_func, cancel_baton, scratch_pool)); (*notify_func)(notify_baton, notify, scratch_pool); } return SVN_NO_ERROR; @@ -282,6 +328,8 @@ copy_versioned_file(svn_wc__db_t *db, data in addition to copying the directory. WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root) + + If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. */ static svn_error_t * copy_versioned_dir(svn_wc__db_t *db, @@ -291,6 +339,7 @@ copy_versioned_dir(svn_wc__db_t *db, const char *tmpdir_abspath, svn_boolean_t metadata_only, svn_boolean_t is_move, + const svn_io_dirent2_t *dirent, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -314,6 +363,7 @@ copy_versioned_dir(svn_wc__db_t *db, tmpdir_abspath, FALSE /* file_copy */, FALSE /* unversioned */, + dirent, SVN_INVALID_FILESIZE, 0, cancel_func, cancel_baton, scratch_pool, scratch_pool)); } @@ -353,6 +403,7 @@ copy_versioned_dir(svn_wc__db_t *db, SVN_ERR(svn_wc__db_read_children_info(&versioned_children, &conflicted_children, db, src_abspath, + FALSE /* base_tree_only */, scratch_pool, iterpool)); for (hi = apr_hash_first(scratch_pool, versioned_children); hi; @@ -366,8 +417,8 @@ copy_versioned_dir(svn_wc__db_t *db, if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); - child_name = svn__apr_hash_index_key(hi); - info = svn__apr_hash_index_val(hi); + child_name = apr_hash_this_key(hi); + info = apr_hash_this_val(hi); child_src_abspath = svn_dirent_join(src_abspath, child_name, iterpool); child_dst_abspath = svn_dirent_join(dst_abspath, child_name, iterpool); @@ -394,6 +445,12 @@ copy_versioned_dir(svn_wc__db_t *db, tmpdir_abspath, metadata_only, info->conflicted, is_move, + disk_children + ? svn_hash_gets(disk_children, + child_name) + : NULL, + info->recorded_size, + info->recorded_time, cancel_func, cancel_baton, NULL, NULL, iterpool)); @@ -403,6 +460,10 @@ copy_versioned_dir(svn_wc__db_t *db, child_src_abspath, child_dst_abspath, dst_op_root_abspath, tmpdir_abspath, metadata_only, is_move, + disk_children + ? svn_hash_gets(disk_children, + child_name) + : NULL, cancel_func, cancel_baton, NULL, NULL, iterpool)); else @@ -421,7 +482,7 @@ copy_versioned_dir(svn_wc__db_t *db, child_dst_abspath, dst_op_root_abspath, is_move, NULL, iterpool)); - /* Don't recurse on children while all we do is creating not-present + /* Don't recurse on children when all we do is creating not-present children */ } else if (info->status == svn_wc__db_status_incomplete) @@ -467,7 +528,7 @@ copy_versioned_dir(svn_wc__db_t *db, for (hi = apr_hash_first(scratch_pool, disk_children); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); + const char *name = apr_hash_this_key(hi); const char *unver_src_abspath, *unver_dst_abspath; svn_skel_t *work_item; @@ -488,6 +549,7 @@ copy_versioned_dir(svn_wc__db_t *db, SVN_ERR(copy_to_tmpdir(&work_item, NULL, db, unver_src_abspath, unver_dst_abspath, tmpdir_abspath, TRUE /* recursive */, TRUE /* unversioned */, + NULL, SVN_INVALID_FILESIZE, 0, cancel_func, cancel_baton, scratch_pool, iterpool)); @@ -507,10 +569,10 @@ copy_versioned_dir(svn_wc__db_t *db, * The additional parameter IS_MOVE indicates whether this is a copy or * a move operation. * - * If MOVE_DEGRADED_TO_COPY is not NULL and a move had to be degraded - * to a copy, then set *MOVE_DEGRADED_TO_COPY. */ + * If RECORD_MOVE_ON_DELETE is not NULL and a move had to be degraded + * to a copy, then set *RECORD_MOVE_ON_DELETE to FALSE. */ static svn_error_t * -copy_or_move(svn_boolean_t *move_degraded_to_copy, +copy_or_move(svn_boolean_t *record_move_on_delete, svn_wc_context_t *wc_ctx, const char *src_abspath, const char *dst_abspath, @@ -533,6 +595,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, svn_boolean_t within_one_wc; svn_wc__db_status_t src_status; svn_error_t *err; + svn_filesize_t recorded_size; + apr_time_t recorded_time; SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath)); SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); @@ -550,7 +614,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, err = svn_wc__db_read_info(&src_status, &src_db_kind, NULL, &src_repos_relpath, &src_repos_root_url, &src_repos_uuid, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + &recorded_size, &recorded_time, NULL, &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, db, src_abspath, scratch_pool, scratch_pool); @@ -643,10 +708,13 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, scratch_pool, scratch_pool)); else /* If not added, the node must have a base or we can't copy */ - SVN_ERR(svn_wc__db_scan_base_repos(NULL, &src_repos_root_url, - &src_repos_uuid, - db, src_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, + &src_repos_root_url, + &src_repos_uuid, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + db, src_abspath, + scratch_pool, scratch_pool)); } if (!dst_repos_root_url) @@ -660,10 +728,13 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, scratch_pool, scratch_pool)); else /* If not added, the node must have a base or we can't copy */ - SVN_ERR(svn_wc__db_scan_base_repos(NULL, &dst_repos_root_url, - &dst_repos_uuid, - db, dstdir_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, + &dst_repos_root_url, + &dst_repos_uuid, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + db, dstdir_abspath, + scratch_pool, scratch_pool)); } if (strcmp(src_repos_root_url, dst_repos_root_url) != 0 @@ -751,8 +822,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, if (is_move && !within_one_wc) { - if (move_degraded_to_copy) - *move_degraded_to_copy = TRUE; + if (record_move_on_delete) + *record_move_on_delete = FALSE; is_move = FALSE; } @@ -768,6 +839,7 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath, tmpdir_abspath, metadata_only, conflicted, is_move, + NULL, recorded_size, recorded_time, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool); @@ -795,14 +867,17 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy, scratch_pool), min_rev, max_rev); +#ifndef RECORD_MIXED_MOVE is_move = FALSE; - if (move_degraded_to_copy) - *move_degraded_to_copy = TRUE; + if (record_move_on_delete) + *record_move_on_delete = FALSE; +#endif } } err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath, tmpdir_abspath, metadata_only, is_move, + NULL /* dirent */, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool); @@ -871,7 +946,8 @@ remove_node_conflict_markers(svn_wc__db_t *db, { svn_skel_t *conflict; - SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, + db, src_abspath, scratch_pool, scratch_pool)); /* Do we have conflict markers that should be removed? */ @@ -923,6 +999,8 @@ static svn_error_t * remove_all_conflict_markers(svn_wc__db_t *db, const char *src_dir_abspath, const char *dst_dir_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *scratch_pool) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -936,14 +1014,18 @@ remove_all_conflict_markers(svn_wc__db_t *db, artillery. */ SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, src_dir_abspath, + FALSE /* base_tree_only */, scratch_pool, iterpool)); for (hi = apr_hash_first(scratch_pool, nodes); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - struct svn_wc__db_info_t *info = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + struct svn_wc__db_info_t *info = apr_hash_this_val(hi); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); if (info->conflicted) { @@ -961,6 +1043,7 @@ remove_all_conflict_markers(svn_wc__db_t *db, db, svn_dirent_join(src_dir_abspath, name, iterpool), svn_dirent_join(dst_dir_abspath, name, iterpool), + cancel_func, cancel_baton, iterpool)); } } @@ -982,7 +1065,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, apr_pool_t *scratch_pool) { svn_wc__db_t *db = wc_ctx->db; - svn_boolean_t move_degraded_to_copy = FALSE; + svn_boolean_t record_on_delete = TRUE; svn_node_kind_t kind; svn_boolean_t conflicted; @@ -994,7 +1077,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, svn_dirent_dirname(dst_abspath, scratch_pool), scratch_pool)); - SVN_ERR(copy_or_move(&move_degraded_to_copy, + SVN_ERR(copy_or_move(&record_on_delete, wc_ctx, src_abspath, dst_abspath, TRUE /* metadata_only */, TRUE /* is_move */, @@ -1018,7 +1101,25 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, is still in a valid state. So be careful when switching this over to the workqueue. */ if (!metadata_only) - SVN_ERR(svn_io_file_rename(src_abspath, dst_abspath, scratch_pool)); + { + svn_error_t *err; + + err = svn_error_trace(svn_io_file_rename(src_abspath, dst_abspath, + scratch_pool)); + + /* Let's try if we can keep wc.db consistent even when the move + fails. Deleting the target is a wc.db only operation, while + going forward (delaying the error) would try to change + conflict markers, which might also fail. */ + if (err) + return svn_error_trace( + svn_error_compose_create( + err, + svn_wc__db_op_delete(wc_ctx->db, dst_abspath, NULL, TRUE, + NULL, NULL, cancel_func, cancel_baton, + NULL, NULL, + scratch_pool))); + } SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1030,6 +1131,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, if (kind == svn_node_dir) SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath, + cancel_func, cancel_baton, scratch_pool)); if (conflicted) @@ -1045,7 +1147,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx, } SVN_ERR(svn_wc__db_op_delete(db, src_abspath, - move_degraded_to_copy ? NULL : dst_abspath, + record_on_delete ? dst_abspath : NULL, TRUE /* delete_dir_externals */, NULL /* conflict */, NULL /* work_items */, cancel_func, cancel_baton, diff --git a/subversion/libsvn_wc/crop.c b/subversion/libsvn_wc/crop.c index a8d5ce2..3a46b42 100644 --- a/subversion/libsvn_wc/crop.c +++ b/subversion/libsvn_wc/crop.c @@ -53,7 +53,7 @@ crop_children(svn_wc__db_t *db, void *notify_baton, svn_cancel_func_t cancel_func, void *cancel_baton, - apr_pool_t *pool) + apr_pool_t *scratch_pool) { const apr_array_header_t *children; apr_pool_t *iterpool; @@ -65,7 +65,7 @@ crop_children(svn_wc__db_t *db, if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); - iterpool = svn_pool_create(pool); + iterpool = svn_pool_create(scratch_pool); if (dir_depth == svn_depth_unknown) dir_depth = svn_depth_infinity; @@ -76,8 +76,8 @@ crop_children(svn_wc__db_t *db, iterpool)); /* Looping over current directory's SVN entries: */ - SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath, pool, - iterpool)); + SVN_ERR(svn_wc__db_base_get_children(&children, db, local_abspath, + scratch_pool, iterpool)); for (i = 0; i < children->nelts; i++) { @@ -86,6 +86,8 @@ crop_children(svn_wc__db_t *db, svn_wc__db_status_t child_status; svn_node_kind_t kind; svn_depth_t child_depth; + svn_boolean_t have_work; + svn_depth_t remove_below; svn_pool_clear(iterpool); @@ -96,86 +98,80 @@ crop_children(svn_wc__db_t *db, NULL,NULL, NULL, NULL, &child_depth, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, &have_work, db, child_abspath, iterpool, iterpool)); - if (child_status == svn_wc__db_status_server_excluded || - child_status == svn_wc__db_status_excluded || - child_status == svn_wc__db_status_not_present) + if (have_work) + { + svn_boolean_t modified, all_deletes; + + if (child_status != svn_wc__db_status_deleted) + continue; /* Leave local additions alone */ + + SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, + db, child_abspath, FALSE, + cancel_func, cancel_baton, + iterpool)); + + if (modified && !all_deletes) + continue; /* Something interesting is still there */ + } + + remove_below = (kind == svn_node_dir) + ? svn_depth_immediates + : svn_depth_files; + + if ((child_status == svn_wc__db_status_server_excluded || + child_status == svn_wc__db_status_excluded || + child_status == svn_wc__db_status_not_present)) { - svn_depth_t remove_below = (kind == svn_node_dir) - ? svn_depth_immediates - : svn_depth_files; if (new_depth < remove_below) SVN_ERR(svn_wc__db_base_remove(db, child_abspath, FALSE /* keep_as_working */, - FALSE /* queue_deletes */, - FALSE /* remove_locks */, + FALSE, FALSE, SVN_INVALID_REVNUM, NULL, NULL, iterpool)); - continue; + continue; /* No recurse */ } - else if (kind == svn_node_file) + + if (new_depth < remove_below) { - if (new_depth == svn_depth_empty) - SVN_ERR(svn_wc__db_op_remove_node(NULL, - db, child_abspath, - TRUE /* destroy */, - FALSE /* destroy_changes */, - SVN_INVALID_REVNUM, - svn_wc__db_status_not_present, - svn_node_none, - NULL, NULL, + svn_boolean_t modified, all_deletes; + + SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, + db, child_abspath, FALSE, cancel_func, cancel_baton, iterpool)); - else - continue; - } - else if (kind == svn_node_dir) - { - if (new_depth < svn_depth_immediates) - { - SVN_ERR(svn_wc__db_op_remove_node(NULL, - db, child_abspath, - TRUE /* destroy */, - FALSE /* destroy_changes */, - SVN_INVALID_REVNUM, - svn_wc__db_status_not_present, - svn_node_none, - NULL, NULL, - cancel_func, cancel_baton, - iterpool)); - } - else + if (!modified || all_deletes) { - SVN_ERR(crop_children(db, - child_abspath, - child_depth, - svn_depth_empty, - notify_func, - notify_baton, - cancel_func, - cancel_baton, - iterpool)); - continue; + SVN_ERR(svn_wc__db_base_remove(db, child_abspath, + FALSE, FALSE, FALSE, + SVN_INVALID_REVNUM, + NULL, NULL, iterpool)); + if (notify_func) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify(child_abspath, + svn_wc_notify_delete, + iterpool); + (*notify_func)(notify_baton, notify, iterpool); + } + + continue; /* No recurse */ } - } - else - { - return svn_error_createf - (SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("Unknown node kind for '%s'"), - svn_dirent_local_style(child_abspath, iterpool)); + + /* Fall through: recurse:*/ } - if (notify_func) + if (kind == svn_node_dir) { - svn_wc_notify_t *notify; - notify = svn_wc_create_notify(child_abspath, - svn_wc_notify_delete, - iterpool); - (*notify_func)(notify_baton, notify, iterpool); + SVN_ERR(crop_children(db, child_abspath, + child_depth, svn_depth_empty, + notify_func, notify_baton, + cancel_func, cancel_baton, + iterpool)); } } @@ -197,6 +193,8 @@ svn_wc_exclude(svn_wc_context_t *wc_ctx, svn_wc__db_status_t status; svn_node_kind_t kind; svn_revnum_t revision; + svn_depth_t depth; + svn_boolean_t modified, all_deletes; const char *repos_relpath, *repos_root, *repos_uuid; SVN_ERR(svn_wc__db_is_switched(&is_root, &is_switched, NULL, @@ -221,7 +219,7 @@ svn_wc_exclude(svn_wc_context_t *wc_ctx, SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, &repos_relpath, &repos_root, &repos_uuid, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &depth, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, wc_ctx->db, local_abspath, @@ -258,29 +256,41 @@ svn_wc_exclude(svn_wc_context_t *wc_ctx, break; /* Ok to exclude */ } - /* Remove all working copy data below local_abspath */ - SVN_ERR(svn_wc__db_op_remove_node(NULL, - wc_ctx->db, local_abspath, - TRUE /* destroy */, - FALSE /* destroy_changes */, - revision, - svn_wc__db_status_excluded, - kind, - NULL, NULL, - cancel_func, cancel_baton, - scratch_pool)); - - SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, - cancel_func, cancel_baton, - scratch_pool)); - - if (notify_func) + SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, + wc_ctx->db, local_abspath, FALSE, + cancel_func, cancel_baton, + scratch_pool)); + + if (!modified || all_deletes) + { + /* Remove all working copy data below local_abspath */ + SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath, + FALSE /* keep_working */, + FALSE, TRUE, + revision, + NULL, NULL, + scratch_pool)); + + SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); + + if (notify_func) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify(local_abspath, + svn_wc_notify_exclude, + scratch_pool); + notify_func(notify_baton, notify, scratch_pool); + } + } + else { - svn_wc_notify_t *notify; - notify = svn_wc_create_notify(local_abspath, - svn_wc_notify_exclude, - scratch_pool); - notify_func(notify_baton, notify, scratch_pool); + /* Do the next best thing: retry below this path */ + SVN_ERR(crop_children(wc_ctx->db, local_abspath, depth, svn_depth_empty, + notify_func, notify_baton, + cancel_func, cancel_baton, + scratch_pool)); } return SVN_NO_ERROR; diff --git a/subversion/libsvn_wc/delete.c b/subversion/libsvn_wc/delete.c index 37c8af0..82ae938 100644 --- a/subversion/libsvn_wc/delete.c +++ b/subversion/libsvn_wc/delete.c @@ -135,7 +135,8 @@ create_delete_wq_items(svn_skel_t **work_items, const apr_array_header_t *markers; int i; - SVN_ERR(svn_wc__db_read_conflict(&conflict, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, + db, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__conflict_read_markers(&markers, db, local_abspath, @@ -431,9 +432,6 @@ svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db, db, local_abspath, destroy_wf /* destroy_wc */, destroy_wf /* destroy_changes */, - SVN_INVALID_REVNUM, - svn_wc__db_status_not_present, - svn_node_none, NULL, NULL, cancel_func, cancel_baton, scratch_pool)); diff --git a/subversion/libsvn_wc/deprecated.c b/subversion/libsvn_wc/deprecated.c index 79cdb30..dcb5e42 100644 --- a/subversion/libsvn_wc/deprecated.c +++ b/subversion/libsvn_wc/deprecated.c @@ -127,7 +127,7 @@ gather_traversal_info(svn_wc_context_t *wc_ctx, hi; hi = apr_hash_next(hi)) { - const char *node_abspath = svn__apr_hash_index_key(hi); + const char *node_abspath = apr_hash_this_key(hi); const char *relpath; relpath = svn_dirent_join(path, @@ -137,11 +137,11 @@ gather_traversal_info(svn_wc_context_t *wc_ctx, if (gather_as_old) svn_hash_sets(traversal_info->externals_old, relpath, - svn__apr_hash_index_val(hi)); + apr_hash_this_val(hi)); if (gather_as_new) svn_hash_sets(traversal_info->externals_new, relpath, - svn__apr_hash_index_val(hi)); + apr_hash_this_val(hi)); svn_hash_sets(traversal_info->depths, relpath, svn_hash_gets(ambient_depths, node_abspath)); @@ -652,6 +652,24 @@ svn_wc_get_pristine_contents(svn_stream_t **contents, return svn_error_trace(svn_wc_context_destroy(wc_ctx)); } +svn_error_t * +svn_wc_queue_committed3(svn_wc_committed_queue_t *queue, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_boolean_t recurse, + const apr_array_header_t *wcprop_changes, + svn_boolean_t remove_lock, + svn_boolean_t remove_changelist, + const svn_checksum_t *sha1_checksum, + apr_pool_t *scratch_pool) +{ + return svn_error_trace( + svn_wc_queue_committed4(queue, wc_ctx, local_abspath, + recurse, TRUE /* is_committed */, + wcprop_changes, remove_lock, + remove_changelist, sha1_checksum, + scratch_pool)); +} svn_error_t * svn_wc_queue_committed2(svn_wc_committed_queue_t *queue, @@ -668,7 +686,9 @@ svn_wc_queue_committed2(svn_wc_committed_queue_t *queue, const char *local_abspath; const svn_checksum_t *sha1_checksum = NULL; - SVN_ERR(svn_wc_context_create(&wc_ctx, NULL, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__context_create_with_db(&wc_ctx, NULL, + svn_wc__adm_get_db(adm_access), + scratch_pool)); SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); if (md5_checksum != NULL) @@ -759,15 +779,11 @@ svn_wc_process_committed4(const char *path, const char *local_abspath; const svn_checksum_t *md5_checksum; const svn_checksum_t *sha1_checksum = NULL; - apr_time_t new_date; - apr_hash_t *wcprop_changes_hash; + svn_wc_context_t *wc_ctx; + svn_wc_committed_queue_t *queue; SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); - - if (rev_date) - SVN_ERR(svn_time_from_cstring(&new_date, rev_date, pool)); - else - new_date = 0; + SVN_ERR(svn_wc__context_create_with_db(&wc_ctx, NULL, db, pool)); if (digest) md5_checksum = svn_checksum__from_digest_md5(digest, pool); @@ -790,15 +806,20 @@ svn_wc_process_committed4(const char *path, SVN_ERR(err); } - wcprop_changes_hash = svn_wc__prop_array_to_hash(wcprop_changes, pool); - SVN_ERR(svn_wc__process_committed_internal(db, local_abspath, recurse, TRUE, - new_revnum, new_date, rev_author, - wcprop_changes_hash, - !remove_lock, !remove_changelist, - sha1_checksum, NULL, pool)); + queue = svn_wc_committed_queue_create(pool); + SVN_ERR(svn_wc_queue_committed3(queue, wc_ctx, local_abspath, recurse, + wcprop_changes, remove_lock, + remove_changelist, + sha1_checksum /* or NULL if not modified + or directory */, + pool)); + + SVN_ERR(svn_wc_process_committed_queue2(queue, wc_ctx, + new_revnum, rev_date, rev_author, + NULL, NULL /* cancel */, + pool)); - /* Run the log file(s) we just created. */ - return svn_error_trace(svn_wc__wq_run(db, local_abspath, NULL, NULL, pool)); + return svn_error_trace(svn_wc_context_destroy(wc_ctx)); } @@ -925,6 +946,19 @@ svn_wc_delete(const char *path, } svn_error_t * +svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx, + const char *local_abspath, + const apr_hash_t *props, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool) +{ + SVN_ERR(svn_wc_add_from_disk3(wc_ctx, local_abspath, props, FALSE, + notify_func, notify_baton, scratch_pool)); + return SVN_NO_ERROR; +} + +svn_error_t * svn_wc_add_from_disk(svn_wc_context_t *wc_ctx, const char *local_abspath, svn_wc_notify_func2_t notify_func, @@ -1025,6 +1059,30 @@ svn_wc_add(const char *path, compat_call_notify_func, &nb, pool); } +/*** From revert.c ***/ +svn_error_t * +svn_wc_revert4(svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_depth_t depth, + svn_boolean_t use_commit_times, + const apr_array_header_t *changelist_filter, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_wc_revert5(wc_ctx, local_abspath, + depth, + use_commit_times, + changelist_filter, + FALSE /* clear_changelists */, + FALSE /* metadata_only */, + cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool)); +} + svn_error_t * svn_wc_revert3(const char *path, svn_wc_adm_access_t *parent_access, @@ -1966,16 +2024,37 @@ svn_wc_get_diff_editor6(const svn_delta_editor_t **editor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + const svn_diff_tree_processor_t *diff_processor; + + /* --git implies --show-copies-as-adds */ + if (use_git_diff_format) + show_copies_as_adds = TRUE; + + /* --show-copies-as-adds implies --notice-ancestry */ + if (show_copies_as_adds) + ignore_ancestry = FALSE; + + SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor, + callbacks, callback_baton, TRUE, + result_pool, scratch_pool)); + + if (reverse_order) + diff_processor = svn_diff__tree_processor_reverse_create( + diff_processor, NULL, result_pool); + + if (! show_copies_as_adds) + diff_processor = svn_diff__tree_processor_copy_as_changed_create( + diff_processor, result_pool); + return svn_error_trace( svn_wc__get_diff_editor(editor, edit_baton, wc_ctx, anchor_abspath, target, depth, - ignore_ancestry, show_copies_as_adds, - use_git_diff_format, use_text_base, + ignore_ancestry, use_text_base, reverse_order, server_performs_filtering, changelist_filter, - callbacks, callback_baton, + diff_processor, cancel_func, cancel_baton, result_pool, scratch_pool)); } @@ -2633,6 +2712,146 @@ svn_wc_props_modified_p(svn_boolean_t *modified_p, } +svn_error_t * +svn_wc__status2_from_3(svn_wc_status2_t **status, + const svn_wc_status3_t *old_status, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const svn_wc_entry_t *entry = NULL; + + if (old_status == NULL) + { + *status = NULL; + return SVN_NO_ERROR; + } + + *status = apr_pcalloc(result_pool, sizeof(**status)); + + if (old_status->versioned) + { + svn_error_t *err; + err= svn_wc__get_entry(&entry, wc_ctx->db, local_abspath, FALSE, + svn_node_unknown, result_pool, scratch_pool); + + if (err && err->apr_err == SVN_ERR_NODE_UNEXPECTED_KIND) + svn_error_clear(err); + else + SVN_ERR(err); + } + + (*status)->entry = entry; + (*status)->copied = old_status->copied; + (*status)->repos_lock = svn_lock_dup(old_status->repos_lock, result_pool); + + if (old_status->repos_relpath) + (*status)->url = svn_path_url_add_component2(old_status->repos_root_url, + old_status->repos_relpath, + result_pool); + (*status)->ood_last_cmt_rev = old_status->ood_changed_rev; + (*status)->ood_last_cmt_date = old_status->ood_changed_date; + (*status)->ood_kind = old_status->ood_kind; + (*status)->ood_last_cmt_author = old_status->ood_changed_author; + + if (old_status->conflicted) + { + const svn_wc_conflict_description2_t *tree_conflict2; + SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict2, wc_ctx, local_abspath, + scratch_pool, scratch_pool)); + (*status)->tree_conflict = svn_wc__cd2_to_cd(tree_conflict2, result_pool); + } + + (*status)->switched = old_status->switched; + + (*status)->text_status = old_status->node_status; + (*status)->prop_status = old_status->prop_status; + + (*status)->repos_text_status = old_status->repos_node_status; + (*status)->repos_prop_status = old_status->repos_prop_status; + + /* Some values might be inherited from properties */ + if (old_status->node_status == svn_wc_status_modified + || old_status->node_status == svn_wc_status_conflicted) + (*status)->text_status = old_status->text_status; + + /* (Currently a no-op, but just make sure it is ok) */ + if (old_status->repos_node_status == svn_wc_status_modified + || old_status->repos_node_status == svn_wc_status_conflicted) + (*status)->repos_text_status = old_status->repos_text_status; + + if (old_status->node_status == svn_wc_status_added) + (*status)->prop_status = svn_wc_status_none; /* No separate info */ + + /* Find pristine_text_status value */ + switch (old_status->text_status) + { + case svn_wc_status_none: + case svn_wc_status_normal: + case svn_wc_status_modified: + (*status)->pristine_text_status = old_status->text_status; + break; + case svn_wc_status_conflicted: + default: + /* ### Fetch compare data, or fall back to the documented + not retrieved behavior? */ + (*status)->pristine_text_status = svn_wc_status_none; + break; + } + + /* Find pristine_prop_status value */ + switch (old_status->prop_status) + { + case svn_wc_status_none: + case svn_wc_status_normal: + case svn_wc_status_modified: + if (old_status->node_status != svn_wc_status_added + && old_status->node_status != svn_wc_status_deleted + && old_status->node_status != svn_wc_status_replaced) + { + (*status)->pristine_prop_status = old_status->prop_status; + } + else + (*status)->pristine_prop_status = svn_wc_status_none; + break; + case svn_wc_status_conflicted: + default: + /* ### Fetch compare data, or fall back to the documented + not retrieved behavior? */ + (*status)->pristine_prop_status = svn_wc_status_none; + break; + } + + if (old_status->versioned + && old_status->conflicted + && old_status->node_status != svn_wc_status_obstructed + && (old_status->kind == svn_node_file + || old_status->node_status != svn_wc_status_missing)) + { + svn_boolean_t text_conflict_p, prop_conflict_p; + + /* The entry says there was a conflict, but the user might have + marked it as resolved by deleting the artifact files, so check + for that. */ + SVN_ERR(svn_wc__internal_conflicted_p(&text_conflict_p, + &prop_conflict_p, + NULL, + wc_ctx->db, local_abspath, + scratch_pool)); + + if (text_conflict_p) + (*status)->text_status = svn_wc_status_conflicted; + + if (prop_conflict_p) + (*status)->prop_status = svn_wc_status_conflicted; + } + + return SVN_NO_ERROR; +} + + + /*** From status.c ***/ struct status4_wrapper_baton @@ -2698,9 +2917,9 @@ svn_wc_get_status_editor5(const svn_delta_editor_t **editor, wc_ctx, anchor_abspath, target_basename, - depth, - get_all, no_ignore, - depth_as_sticky, + depth, get_all, + TRUE, /* check_working_copy */ + no_ignore, depth_as_sticky, server_performs_filtering, ignore_patterns, status_func, status_baton, @@ -4044,7 +4263,26 @@ svn_wc_relocate(const char *path, } -/*** From log.c ***/ +/*** From log.c / cleanup.c ***/ + +svn_error_t * +svn_wc_cleanup3(svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + return svn_error_trace( + svn_wc_cleanup4(wc_ctx, + local_abspath, + TRUE /* break_locks */, + TRUE /* fix_recorded_timestamps */, + TRUE /* clear_dav_cache */, + TRUE /* clean_pristines */, + cancel_func, cancel_baton, + NULL, NULL /* notify */, + scratch_pool)); +} svn_error_t * svn_wc_cleanup2(const char *path, @@ -4570,13 +4808,11 @@ svn_wc_read_kind(svn_node_kind_t *kind, TRUE /* show_deleted */, show_hidden, scratch_pool)); +} - /*if (db_kind == svn_node_dir) - *kind = svn_node_dir; - else if (db_kind == svn_node_file || db_kind == svn_node_symlink) - *kind = svn_node_file; - else - *kind = svn_node_none;*/ - - return SVN_NO_ERROR; +svn_wc_conflict_description2_t * +svn_wc__conflict_description2_dup(const svn_wc_conflict_description2_t *conflict, + apr_pool_t *pool) +{ + return svn_wc_conflict_description2_dup(conflict, pool); } diff --git a/subversion/libsvn_wc/diff.h b/subversion/libsvn_wc/diff.h index 3ddada6..2df88e1 100644 --- a/subversion/libsvn_wc/diff.h +++ b/subversion/libsvn_wc/diff.h @@ -39,7 +39,7 @@ extern "C" { #endif /* __cplusplus */ /* A function to diff locally added and locally copied files. - + Reports the file LOCAL_ABSPATH as ADDED file with relpath RELPATH to PROCESSOR with as parent baton PROCESSOR_PARENT_BATON. @@ -60,7 +60,7 @@ svn_wc__diff_local_only_file(svn_wc__db_t *db, apr_pool_t *scratch_pool); /* A function to diff locally added and locally copied directories. - + Reports the directory LOCAL_ABSPATH and everything below it (limited by DEPTH) as added with relpath RELPATH to PROCESSOR with as parent baton PROCESSOR_PARENT_BATON. diff --git a/subversion/libsvn_wc/diff_editor.c b/subversion/libsvn_wc/diff_editor.c index c9078ed..fc059a5 100644 --- a/subversion/libsvn_wc/diff_editor.c +++ b/subversion/libsvn_wc/diff_editor.c @@ -64,10 +64,11 @@ #include "svn_hash.h" #include "svn_sorts.h" -#include "private/svn_subr_private.h" -#include "private/svn_wc_private.h" #include "private/svn_diff_tree.h" #include "private/svn_editor.h" +#include "private/svn_sorts_private.h" +#include "private/svn_subr_private.h" +#include "private/svn_wc_private.h" #include "wc.h" #include "props.h" @@ -241,10 +242,9 @@ make_edit_baton(struct edit_baton_t **edit_baton, svn_wc__db_t *db, const char *anchor_abspath, const char *target, - const svn_diff_tree_processor_t *processor, + const svn_diff_tree_processor_t *diff_processor, svn_depth_t depth, svn_boolean_t ignore_ancestry, - svn_boolean_t show_copies_as_adds, svn_boolean_t use_text_base, svn_boolean_t reverse_order, svn_cancel_func_t cancel_func, @@ -255,22 +255,11 @@ make_edit_baton(struct edit_baton_t **edit_baton, SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath)); - if (reverse_order) - processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool); - - /* --show-copies-as-adds implies --notice-ancestry */ - if (show_copies_as_adds) - ignore_ancestry = FALSE; - - if (! show_copies_as_adds) - processor = svn_diff__tree_processor_copy_as_changed_create(processor, - pool); - eb = apr_pcalloc(pool, sizeof(*eb)); eb->db = db; eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath); eb->target = apr_pstrdup(pool, target); - eb->processor = processor; + eb->processor = diff_processor; eb->depth = depth; eb->ignore_ancestry = ignore_ancestry; eb->local_before_remote = reverse_order; @@ -566,6 +555,7 @@ ensure_local_info(struct dir_baton_t *db, SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts, db->eb->db, db->local_abspath, + FALSE /* base_tree_only */, db->pool, scratch_pool)); return SVN_NO_ERROR; @@ -655,6 +645,7 @@ walk_local_nodes_diff(struct edit_baton_t *eb, SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath, + FALSE /* base_tree_only */, scratch_pool, iterpool)); children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, @@ -703,6 +694,9 @@ walk_local_nodes_diff(struct edit_baton_t *eb, if (!info->have_base) { local_only = TRUE; /* Only report additions */ + + if (info->status == svn_wc__db_status_deleted) + continue; /* Nothing added (deleted copy) */ } else if (info->status == svn_wc__db_status_normal) { @@ -1042,9 +1036,6 @@ svn_wc__diff_local_only_dir(svn_wc__db_t *db, svn_boolean_t skip_children = FALSE; svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); - svn_depth_t depth_below_here = depth; - apr_hash_t *nodes; - apr_hash_t *conflicts; SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1098,70 +1089,82 @@ svn_wc__diff_local_only_dir(svn_wc__db_t *db, processor_parent_baton, processor, scratch_pool, iterpool)); - /* ### skip_children is not used */ - - SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath, - scratch_pool, iterpool)); - - if (depth_below_here == svn_depth_immediates) - depth_below_here = svn_depth_empty; - children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, - scratch_pool); - - for (i = 0; i < children->nelts; i++) + if ((depth > svn_depth_empty || depth == svn_depth_unknown) + && ! skip_children) { - svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t); - const char *name = item->key; - struct svn_wc__db_info_t *info = item->value; - const char *child_abspath; - const char *child_relpath; + svn_depth_t depth_below_here = depth; + apr_hash_t *nodes; + apr_hash_t *conflicts; - svn_pool_clear(iterpool); + if (depth_below_here == svn_depth_immediates) + depth_below_here = svn_depth_empty; + + SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, + db, local_abspath, + FALSE /* base_tree_only */, + scratch_pool, iterpool)); - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - child_abspath = svn_dirent_join(local_abspath, name, iterpool); + children = svn_sort__hash(nodes, svn_sort_compare_items_lexically, + scratch_pool); - if (NOT_PRESENT(info->status)) + for (i = 0; i < children->nelts; i++) { - continue; - } + svn_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t); + const char *name = item->key; + struct svn_wc__db_info_t *info = item->value; + const char *child_abspath; + const char *child_relpath; - /* If comparing against WORKING, skip entries that are - schedule-deleted - they don't really exist. */ - if (!diff_pristine && info->status == svn_wc__db_status_deleted) - continue; + svn_pool_clear(iterpool); - child_relpath = svn_relpath_join(relpath, name, iterpool); + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); - switch (info->kind) - { - case svn_node_file: - case svn_node_symlink: - SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, - child_relpath, - processor, pdb, - diff_pristine, - cancel_func, cancel_baton, - scratch_pool)); - break; + child_abspath = svn_dirent_join(local_abspath, name, iterpool); - case svn_node_dir: - if (depth > svn_depth_files || depth == svn_depth_unknown) + if (NOT_PRESENT(info->status)) { - SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, - child_relpath, depth_below_here, - processor, pdb, - diff_pristine, - cancel_func, cancel_baton, - iterpool)); + continue; } - break; - default: - break; + /* If comparing against WORKING, skip entries that are + schedule-deleted - they don't really exist. */ + if (!diff_pristine && info->status == svn_wc__db_status_deleted) + continue; + + child_relpath = svn_relpath_join(relpath, name, iterpool); + + switch (info->kind) + { + case svn_node_file: + case svn_node_symlink: + SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, + child_relpath, + processor, pdb, + diff_pristine, + cancel_func, cancel_baton, + scratch_pool)); + break; + + case svn_node_dir: + if (depth > svn_depth_files || depth == svn_depth_unknown) + { + SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, + child_relpath, + depth_below_here, + processor, pdb, + diff_pristine, + cancel_func, + cancel_baton, + iterpool)); + } + break; + + default: + break; + } } } @@ -2193,7 +2196,7 @@ change_file_prop(void *file_baton, propchange = apr_array_push(fb->propchanges); propchange->name = apr_pstrdup(fb->pool, name); - propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; + propchange->value = svn_string_dup(value, fb->pool); return SVN_NO_ERROR; } @@ -2218,7 +2221,7 @@ change_dir_prop(void *dir_baton, propchange = apr_array_push(db->propchanges); propchange->name = apr_pstrdup(db->pool, name); - propchange->value = value ? svn_string_dup(value, db->pool) : NULL; + propchange->value = svn_string_dup(value, db->pool); return SVN_NO_ERROR; } @@ -2257,14 +2260,11 @@ svn_wc__get_diff_editor(const svn_delta_editor_t **editor, const char *target, svn_depth_t depth, svn_boolean_t ignore_ancestry, - svn_boolean_t show_copies_as_adds, - svn_boolean_t use_git_diff_format, svn_boolean_t use_text_base, svn_boolean_t reverse_order, svn_boolean_t server_performs_filtering, const apr_array_header_t *changelist_filter, - const svn_wc_diff_callbacks4_t *callbacks, - void *callback_baton, + const svn_diff_tree_processor_t *diff_processor, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, @@ -2277,18 +2277,9 @@ svn_wc__get_diff_editor(const svn_delta_editor_t **editor, struct svn_wc__shim_fetch_baton_t *sfb; svn_delta_shim_callbacks_t *shim_callbacks = svn_delta_shim_callbacks_default(result_pool); - const svn_diff_tree_processor_t *diff_processor; SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath)); - /* --git implies --show-copies-as-adds */ - if (use_git_diff_format) - show_copies_as_adds = TRUE; - - SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor, - callbacks, callback_baton, TRUE, - result_pool, scratch_pool)); - /* Apply changelist filtering to the output */ if (changelist_filter && changelist_filter->nelts) { @@ -2305,7 +2296,7 @@ svn_wc__get_diff_editor(const svn_delta_editor_t **editor, wc_ctx->db, anchor_abspath, target, diff_processor, - depth, ignore_ancestry, show_copies_as_adds, + depth, ignore_ancestry, use_text_base, reverse_order, cancel_func, cancel_baton, result_pool)); @@ -2458,8 +2449,8 @@ wrap_dir_opened(void **new_dir_baton, /* svn_diff_tree_processor_t function */ static svn_error_t * wrap_dir_added(const char *relpath, - const svn_diff_source_t *right_source, const svn_diff_source_t *copyfrom_source, + const svn_diff_source_t *right_source, /*const*/ apr_hash_t *copyfrom_props, /*const*/ apr_hash_t *right_props, void *dir_baton, diff --git a/subversion/libsvn_wc/diff_local.c b/subversion/libsvn_wc/diff_local.c index 22b498f..e1cb329 100644 --- a/subversion/libsvn_wc/diff_local.c +++ b/subversion/libsvn_wc/diff_local.c @@ -1,6 +1,6 @@ /* - * diff_pristine.c -- A simple diff walker which compares local files against - * their pristine versions. + * diff_local.c -- A simple diff walker which compares local files against + * their pristine versions. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one @@ -41,8 +41,8 @@ #include "private/svn_diff_tree.h" #include "wc.h" +#include "wc_db.h" #include "props.h" -#include "translate.h" #include "diff.h" #include "svn_private_config.h" @@ -89,9 +89,6 @@ struct diff_baton /* Should this diff ignore node ancestry? */ svn_boolean_t ignore_ancestry; - /* Should this diff not compare copied files with their source? */ - svn_boolean_t show_copies_as_adds; - /* Cancel function/baton */ svn_cancel_func_t cancel_func; void *cancel_baton; @@ -119,17 +116,17 @@ ensure_state(struct diff_baton *eb, if (! relpath) return SVN_NO_ERROR; - /* Don't recurse on the anchor, as that might loop infinately because + /* Don't recurse on the anchor, as that might loop infinitely because svn_dirent_dirname("/",...) -> "/" svn_dirent_dirname("C:/",...) -> "C:/" (Windows) */ if (*relpath) SVN_ERR(ensure_state(eb, - svn_dirent_dirname(local_abspath,scratch_pool), + svn_dirent_dirname(local_abspath, scratch_pool), FALSE, scratch_pool)); } else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL)) - SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), + SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool), FALSE, scratch_pool)); else @@ -421,23 +418,22 @@ diff_status_callback(void *baton, /* Public Interface */ svn_error_t * -svn_wc_diff6(svn_wc_context_t *wc_ctx, - const char *local_abspath, - const svn_wc_diff_callbacks4_t *callbacks, - void *callback_baton, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - svn_boolean_t show_copies_as_adds, - svn_boolean_t use_git_diff_format, - const apr_array_header_t *changelist_filter, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) +svn_wc__diff7(const char **root_relpath, + svn_boolean_t *root_is_dir, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + const apr_array_header_t *changelist_filter, + const svn_diff_tree_processor_t *diff_processor, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { struct diff_baton eb = { 0 }; svn_node_kind_t kind; svn_boolean_t get_all; - const svn_diff_tree_processor_t *processor; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath, @@ -446,30 +442,27 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, FALSE /* show_hidden */, scratch_pool)); - if (kind == svn_node_dir) - eb.anchor_abspath = local_abspath; - else - eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool); - - SVN_ERR(svn_wc__wrap_diff_callbacks(&processor, - callbacks, callback_baton, TRUE, - scratch_pool, scratch_pool)); - - if (use_git_diff_format) - show_copies_as_adds = TRUE; - if (show_copies_as_adds) - ignore_ancestry = FALSE; + eb.anchor_abspath = local_abspath; + if (root_relpath) + { + svn_boolean_t is_wcroot; + SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, + wc_ctx->db, local_abspath, scratch_pool)); - /* - if (reverse_order) - processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool); - */ + if (!is_wcroot) + eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool); + } + else if (kind != svn_node_dir) + eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool); - if (! show_copies_as_adds && !use_git_diff_format) - processor = svn_diff__tree_processor_copy_as_changed_create(processor, - scratch_pool); + if (root_relpath) + *root_relpath = apr_pstrdup(result_pool, + svn_dirent_skip_ancestor(eb.anchor_abspath, + local_abspath)); + if (root_is_dir) + *root_is_dir = (kind == svn_node_dir); /* Apply changelist filtering to the output */ if (changelist_filter && changelist_filter->nelts) @@ -477,19 +470,18 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, apr_hash_t *changelist_hash; SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter, - scratch_pool)); - processor = svn_wc__changelist_filter_tree_processor_create( - processor, wc_ctx, local_abspath, - changelist_hash, scratch_pool); + result_pool)); + diff_processor = svn_wc__changelist_filter_tree_processor_create( + diff_processor, wc_ctx, local_abspath, + changelist_hash, result_pool); } eb.db = wc_ctx->db; - eb.processor = processor; + eb.processor = diff_processor; eb.ignore_ancestry = ignore_ancestry; - eb.show_copies_as_adds = show_copies_as_adds; eb.pool = scratch_pool; - if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry) + if (ignore_ancestry) get_all = TRUE; /* We need unmodified descendants of copies */ else get_all = FALSE; @@ -512,22 +504,22 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, if (!ns->skip) { if (ns->propchanges) - SVN_ERR(processor->dir_changed(ns->relpath, - ns->left_src, - ns->right_src, - ns->left_props, - ns->right_props, - ns->propchanges, - ns->baton, - processor, - ns->pool)); + SVN_ERR(diff_processor->dir_changed(ns->relpath, + ns->left_src, + ns->right_src, + ns->left_props, + ns->right_props, + ns->propchanges, + ns->baton, + diff_processor, + ns->pool)); else - SVN_ERR(processor->dir_closed(ns->relpath, - ns->left_src, - ns->right_src, - ns->baton, - processor, - ns->pool)); + SVN_ERR(diff_processor->dir_closed(ns->relpath, + ns->left_src, + ns->right_src, + ns->baton, + diff_processor, + ns->pool)); } eb.cur = ns->parent; svn_pool_clear(ns->pool); @@ -535,3 +527,43 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, return SVN_NO_ERROR; } + +svn_error_t * +svn_wc_diff6(svn_wc_context_t *wc_ctx, + const char *local_abspath, + const svn_wc_diff_callbacks4_t *callbacks, + void *callback_baton, + svn_depth_t depth, + svn_boolean_t ignore_ancestry, + svn_boolean_t show_copies_as_adds, + svn_boolean_t use_git_diff_format, + const apr_array_header_t *changelist_filter, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + const svn_diff_tree_processor_t *processor; + + SVN_ERR(svn_wc__wrap_diff_callbacks(&processor, + callbacks, callback_baton, TRUE, + scratch_pool, scratch_pool)); + + if (use_git_diff_format) + show_copies_as_adds = TRUE; + if (show_copies_as_adds) + ignore_ancestry = FALSE; + + if (! show_copies_as_adds && !use_git_diff_format) + processor = svn_diff__tree_processor_copy_as_changed_create(processor, + scratch_pool); + + return svn_error_trace(svn_wc__diff7(NULL, NULL, + wc_ctx, local_abspath, + depth, + ignore_ancestry, + changelist_filter, + processor, + cancel_func, cancel_baton, + scratch_pool, scratch_pool)); +} + diff --git a/subversion/libsvn_wc/entries.c b/subversion/libsvn_wc/entries.c index 24dae50..1be6a8b 100644 --- a/subversion/libsvn_wc/entries.c +++ b/subversion/libsvn_wc/entries.c @@ -45,9 +45,14 @@ #include "wc_db.h" #include "wc-queries.h" /* for STMT_* */ +#define SVN_WC__I_AM_WC_DB + #include "svn_private_config.h" #include "private/svn_wc_private.h" #include "private/svn_sqlite.h" +#include "token-map.h" + +#include "wc_db_private.h" #define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x)))) @@ -213,6 +218,8 @@ get_info_for_deleted(svn_wc_entry_t *entry, svn_wc__db_lock_t **lock, svn_wc__db_t *db, const char *entry_abspath, + svn_wc__db_wcroot_t *wcroot, + const char *entry_relpath, const svn_wc_entry_t *parent_entry, svn_boolean_t have_base, svn_boolean_t have_more_work, @@ -221,12 +228,13 @@ get_info_for_deleted(svn_wc_entry_t *entry, { if (have_base && !have_more_work) { + apr_int64_t repos_id; /* This is the delete of a BASE node */ - SVN_ERR(svn_wc__db_base_get_info(NULL, kind, + SVN_ERR(svn_wc__db_base_get_info_internal( + NULL, kind, &entry->revision, repos_relpath, - &entry->repos, - &entry->uuid, + &repos_id, &entry->cmt_rev, &entry->cmt_date, &entry->cmt_author, @@ -236,16 +244,18 @@ get_info_for_deleted(svn_wc_entry_t *entry, lock, &entry->has_props, NULL, NULL, - db, - entry_abspath, + wcroot, entry_relpath, result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); } else { - const char *work_del_abspath; + const char *work_del_relpath; const char *parent_repos_relpath; - const char *parent_abspath; + const char *parent_relpath; + apr_int64_t repos_id; /* This is a deleted child of a copy/move-here, so we need to scan up the WORKING tree to find the root of @@ -265,30 +275,33 @@ get_info_for_deleted(svn_wc_entry_t *entry, scratch_pool)); /* working_size and text_time unavailable */ - SVN_ERR(svn_wc__db_scan_deletion(NULL, + SVN_ERR(svn_wc__db_scan_deletion_internal( NULL, - &work_del_abspath, NULL, - db, entry_abspath, + NULL, + &work_del_relpath, NULL, + wcroot, entry_relpath, scratch_pool, scratch_pool)); - SVN_ERR_ASSERT(work_del_abspath != NULL); - parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool); + SVN_ERR_ASSERT(work_del_relpath != NULL); + parent_relpath = svn_relpath_dirname(work_del_relpath, scratch_pool); /* The parent directory of the delete root must be added, so we can find the required information there */ - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, + SVN_ERR(svn_wc__db_scan_addition_internal( + NULL, NULL, &parent_repos_relpath, - &entry->repos, - &entry->uuid, - NULL, NULL, NULL, NULL, - db, parent_abspath, + &repos_id, + NULL, NULL, NULL, + wcroot, parent_relpath, result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); /* Now glue it all together */ *repos_relpath = svn_relpath_join(parent_repos_relpath, - svn_dirent_is_child(parent_abspath, - entry_abspath, - NULL), + svn_relpath_skip_ancestor( + parent_relpath, + entry_relpath), result_pool); @@ -297,11 +310,12 @@ get_info_for_deleted(svn_wc_entry_t *entry, if (have_base) { svn_wc__db_status_t status; - SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision, + SVN_ERR(svn_wc__db_base_get_info_internal( + &status, NULL, &entry->revision, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, lock, NULL, NULL, + NULL, NULL, lock, NULL, NULL, NULL, - db, entry_abspath, + wcroot, entry_relpath, result_pool, scratch_pool)); if (status == svn_wc__db_status_not_present) @@ -346,7 +360,7 @@ write_tree_conflicts(const char **conflict_data, { svn_skel_t *c_skel; - SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi), + SVN_ERR(svn_wc__serialize_conflict(&c_skel, apr_hash_this_val(hi), pool, pool)); svn_skel__prepend(c_skel, skel); } @@ -369,12 +383,17 @@ write_tree_conflicts(const char **conflict_data, If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise, it should refer to the entry for the child's parent directory. + ### All database read operations should really use wcroot, dir_relpath, + as that restores obstruction compatibility with <= 1.6.0 + but that has been the case since the introduction of WC-NG in 1.7.0 + Temporary allocations are made in SCRATCH_POOL. */ static svn_error_t * read_one_entry(const svn_wc_entry_t **new_entry, svn_wc__db_t *db, - apr_int64_t wc_id, const char *dir_abspath, + svn_wc__db_wcroot_t *wcroot, + const char *dir_relpath, const char *name, const svn_wc_entry_t *parent_entry, apr_pool_t *result_pool, @@ -387,24 +406,28 @@ read_one_entry(const svn_wc_entry_t **new_entry, const svn_checksum_t *checksum; svn_filesize_t translated_size; svn_wc_entry_t *entry = alloc_entry(result_pool); + const char *entry_relpath; const char *entry_abspath; + apr_int64_t repos_id; + apr_int64_t original_repos_id; const char *original_repos_relpath; const char *original_root_url; svn_boolean_t conflicted; svn_boolean_t have_base; svn_boolean_t have_more_work; + svn_boolean_t op_root; - entry->name = name; + entry->name = apr_pstrdup(result_pool, name); + entry_relpath = svn_relpath_join(dir_relpath, entry->name, scratch_pool); entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool); - SVN_ERR(svn_wc__db_read_info( + SVN_ERR(svn_wc__db_read_info_internal( &status, &kind, &entry->revision, &repos_relpath, - &entry->repos, - &entry->uuid, + &repos_id, &entry->cmt_rev, &entry->cmt_date, &entry->cmt_author, @@ -412,24 +435,27 @@ read_one_entry(const svn_wc_entry_t **new_entry, &checksum, NULL, &original_repos_relpath, - &original_root_url, - NULL, + &original_repos_id, &entry->copyfrom_rev, &lock, &translated_size, &entry->text_time, &entry->changelist, &conflicted, - NULL /* op_root */, + &op_root, &entry->has_props /* have_props */, &entry->has_prop_mods /* props_mod */, &have_base, &have_more_work, NULL /* have_work */, - db, - entry_abspath, - result_pool, - scratch_pool)); + wcroot, entry_relpath, + result_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&original_root_url, NULL, + wcroot, original_repos_id, + result_pool)); if (entry->has_prop_mods) entry->has_props = TRUE; @@ -457,9 +483,10 @@ read_one_entry(const svn_wc_entry_t **new_entry, child_abspath = svn_dirent_join(dir_abspath, child_name, scratch_pool); - SVN_ERR(svn_wc__read_conflicts(&child_conflicts, + SVN_ERR(svn_wc__read_conflicts(&child_conflicts, NULL, db, child_abspath, FALSE /* create tempfiles */, + TRUE /* tree_conflicts_only */, scratch_pool, scratch_pool)); for (j = 0; j < child_conflicts->nelts; j++) @@ -493,13 +520,15 @@ read_one_entry(const svn_wc_entry_t **new_entry, /* Grab inherited repository information, if necessary. */ if (repos_relpath == NULL) { - SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath, - &entry->repos, - &entry->uuid, - db, - entry_abspath, - result_pool, - scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info_internal( + NULL, NULL, NULL, &repos_relpath, + &repos_id, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, entry_relpath, + result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); } entry->incomplete = (status == svn_wc__db_status_incomplete); @@ -519,13 +548,14 @@ read_one_entry(const svn_wc_entry_t **new_entry, entry->copied = FALSE; else { - const char *work_del_abspath; - SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, - &work_del_abspath, NULL, - db, entry_abspath, + const char *work_del_relpath; + SVN_ERR(svn_wc__db_scan_deletion_internal( + NULL, NULL, + &work_del_relpath, NULL, + wcroot, entry_relpath, scratch_pool, scratch_pool)); - if (work_del_abspath) + if (work_del_relpath) entry->copied = TRUE; } @@ -563,13 +593,14 @@ read_one_entry(const svn_wc_entry_t **new_entry, /* ENTRY->REVISION is overloaded. When a node is schedule-add or -replace, then REVISION refers to the BASE node's revision that is being overwritten. We need to fetch it now. */ - SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, + SVN_ERR(svn_wc__db_base_get_info_internal( + &base_status, NULL, &entry->revision, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - db, entry_abspath, + NULL, NULL, NULL, + wcroot, entry_relpath, scratch_pool, scratch_pool)); @@ -603,18 +634,27 @@ read_one_entry(const svn_wc_entry_t **new_entry, have important data. Set up stuff to kill that idea off, and finish up this entry. */ { - SVN_ERR(svn_wc__db_scan_addition(&work_status, - &op_root_abspath, + const char *op_root_relpath; + SVN_ERR(svn_wc__db_scan_addition_internal( + &work_status, + &op_root_relpath, &repos_relpath, - &entry->repos, - &entry->uuid, + &repos_id, &scanned_original_relpath, - NULL, NULL, /* original_root|uuid */ + NULL /* original_repos_id */, &original_revision, - db, - entry_abspath, + wcroot, entry_relpath, result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, + wcroot, repos_id, result_pool)); + + if (!op_root_relpath) + op_root_abspath = NULL; + else + op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath, + scratch_pool); + /* In wc.db we want to keep the valid revision of the not-present BASE_REV, but when we used entries we set the revision to 0 when adding a new node over a not present base node. */ @@ -633,7 +673,7 @@ read_one_entry(const svn_wc_entry_t **new_entry, /* ### scan_addition may need to be updated to avoid returning ### status_copied in this case. */ } - /* For backwards-compatiblity purposes we treat moves just like + /* For backwards-compatibility purposes we treat moves just like * regular copies. */ else if (work_status == svn_wc__db_status_copied || work_status == svn_wc__db_status_moved_here) @@ -675,10 +715,12 @@ read_one_entry(const svn_wc_entry_t **new_entry, mixed-revision situation. */ if (!is_copied_child) { - const char *parent_abspath; + const char *parent_relpath; svn_error_t *err; const char *parent_repos_relpath; const char *parent_root_url; + apr_int64_t parent_repos_id; + const char *op_root_relpath; /* When we insert entries into the database, we will construct additional copyfrom records for mixed-revision @@ -705,15 +747,16 @@ read_one_entry(const svn_wc_entry_t **new_entry, Note that the parent could be added/copied/moved-here. There is no way for it to be deleted/moved-away and have *this* node appear as copied. */ - parent_abspath = svn_dirent_dirname(entry_abspath, - scratch_pool); - err = svn_wc__db_scan_addition(NULL, - &op_root_abspath, - NULL, NULL, NULL, - &parent_repos_relpath, - &parent_root_url, + parent_relpath = svn_relpath_dirname(entry_relpath, + scratch_pool); + err = svn_wc__db_scan_addition_internal( + NULL, + &op_root_relpath, NULL, NULL, - db, parent_abspath, + &parent_repos_relpath, + &parent_repos_id, + NULL, + wcroot, parent_relpath, scratch_pool, scratch_pool); if (err) @@ -721,10 +764,24 @@ read_one_entry(const svn_wc_entry_t **new_entry, if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); svn_error_clear(err); + op_root_abspath = NULL; + parent_repos_relpath = NULL; + parent_root_url = NULL; + } + else + { + SVN_ERR(svn_wc__db_fetch_repos_info(&parent_root_url, NULL, + wcroot, parent_repos_id, + scratch_pool)); + op_root_abspath = svn_dirent_join(wcroot->abspath, + op_root_relpath, + scratch_pool); } - else if (parent_root_url != NULL + + if (parent_root_url != NULL && strcmp(original_root_url, parent_root_url) == 0) { + const char *relpath_to_entry = svn_dirent_is_child( op_root_abspath, entry_abspath, NULL); const char *entry_repos_relpath = svn_relpath_join( @@ -827,6 +884,7 @@ read_one_entry(const svn_wc_entry_t **new_entry, &checksum, &lock, db, entry_abspath, + wcroot, entry_relpath, parent_entry, have_base, have_more_work, result_pool, scratch_pool)); @@ -869,7 +927,7 @@ read_one_entry(const svn_wc_entry_t **new_entry, /* We got a SHA-1, get the corresponding MD-5. */ if (checksum->kind != svn_checksum_md5) SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db, - entry_abspath, checksum, + dir_abspath, checksum, scratch_pool, scratch_pool)); SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5); @@ -881,8 +939,9 @@ read_one_entry(const svn_wc_entry_t **new_entry, svn_skel_t *conflict; svn_boolean_t text_conflicted; svn_boolean_t prop_conflicted; - SVN_ERR(svn_wc__db_read_conflict(&conflict, db, entry_abspath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, entry_relpath, + scratch_pool, scratch_pool)); SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted, &prop_conflicted, NULL, @@ -955,7 +1014,9 @@ read_one_entry(const svn_wc_entry_t **new_entry, static svn_error_t * read_entries_new(apr_hash_t **result_entries, svn_wc__db_t *db, - const char *local_abspath, + const char *dir_abspath, + svn_wc__db_wcroot_t *wcroot, + const char *dir_relpath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -964,11 +1025,12 @@ read_entries_new(apr_hash_t **result_entries, apr_pool_t *iterpool = svn_pool_create(scratch_pool); int i; const svn_wc_entry_t *parent_entry; - apr_int64_t wc_id = 1; /* ### hacky. should remove. */ entries = apr_hash_make(result_pool); - SVN_ERR(read_one_entry(&parent_entry, db, wc_id, local_abspath, + SVN_ERR(read_one_entry(&parent_entry, + db, dir_abspath, + wcroot, dir_relpath, "" /* name */, NULL /* parent_entry */, result_pool, iterpool)); @@ -977,8 +1039,8 @@ read_entries_new(apr_hash_t **result_entries, /* Use result_pool so that the child names (used by reference, rather than copied) appear in result_pool. */ SVN_ERR(svn_wc__db_read_children(&children, db, - local_abspath, - result_pool, iterpool)); + dir_abspath, + scratch_pool, iterpool)); for (i = children->nelts; i--; ) { const char *name = APR_ARRAY_IDX(children, i, const char *); @@ -987,7 +1049,9 @@ read_entries_new(apr_hash_t **result_entries, svn_pool_clear(iterpool); SVN_ERR(read_one_entry(&entry, - db, wc_id, local_abspath, name, parent_entry, + db, dir_abspath, + wcroot, dir_relpath, + name, parent_entry, result_pool, iterpool)); svn_hash_sets(entries, entry->name, entry); } @@ -1000,28 +1064,20 @@ read_entries_new(apr_hash_t **result_entries, } -/* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return - the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The - two returned pointers will be the same if NAME=="" ("this dir"). - - The parent entry must exist. - - The requested entry MAY exist. If it does not, then NULL will be returned. - - The resulting entries are allocated in RESULT_POOL, and all temporary - allocations are made in SCRATCH_POOL. */ static svn_error_t * -read_entry_pair(const svn_wc_entry_t **parent_entry, - const svn_wc_entry_t **entry, - svn_wc__db_t *db, - const char *dir_abspath, - const char *name, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +read_entry_pair_txn(const svn_wc_entry_t **parent_entry, + const svn_wc_entry_t **entry, + svn_wc__db_t *db, + const char *dir_abspath, + svn_wc__db_wcroot_t *wcroot, + const char *dir_relpath, + const char *name, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_int64_t wc_id = 1; /* ### hacky. should remove. */ - - SVN_ERR(read_one_entry(parent_entry, db, wc_id, dir_abspath, + SVN_ERR(read_one_entry(parent_entry, + db, dir_abspath, + wcroot, dir_relpath, "" /* name */, NULL /* parent_entry */, result_pool, scratch_pool)); @@ -1073,7 +1129,9 @@ read_entry_pair(const svn_wc_entry_t **parent_entry, svn_error_t *err; err = read_one_entry(entry, - db, wc_id, dir_abspath, name, *parent_entry, + db, dir_abspath, + wcroot, dir_relpath, + name, *parent_entry, result_pool, scratch_pool); if (err) { @@ -1096,28 +1154,76 @@ read_entry_pair(const svn_wc_entry_t **parent_entry, return SVN_NO_ERROR; } +/* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return + the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The + two returned pointers will be the same if NAME=="" ("this dir"). + + The parent entry must exist. + + The requested entry MAY exist. If it does not, then NULL will be returned. + + The resulting entries are allocated in RESULT_POOL, and all temporary + allocations are made in SCRATCH_POOL. */ +static svn_error_t * +read_entry_pair(const svn_wc_entry_t **parent_entry, + const svn_wc_entry_t **entry, + svn_wc__db_t *db, + const char *dir_abspath, + const char *name, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *dir_relpath; + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, + db, dir_abspath, + scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_WC__DB_WITH_TXN(read_entry_pair_txn(parent_entry, entry, + db, dir_abspath, + wcroot, dir_relpath, + name, + result_pool, scratch_pool), + wcroot); + + return SVN_NO_ERROR; +} /* */ static svn_error_t * read_entries(apr_hash_t **entries, svn_wc__db_t *db, - const char *wcroot_abspath, + const char *dir_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_wc__db_wcroot_t *wcroot; + const char *dir_relpath; int wc_format; - SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath, + SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, dir_abspath, scratch_pool)); if (wc_format < SVN_WC__WC_NG_VERSION) return svn_error_trace(svn_wc__read_entries_old(entries, - wcroot_abspath, + dir_abspath, result_pool, scratch_pool)); - return svn_error_trace(read_entries_new(entries, db, wcroot_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, + db, dir_abspath, + scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_WC__DB_WITH_TXN(read_entries_new(entries, + db, dir_abspath, + wcroot, dir_relpath, + result_pool, scratch_pool), + wcroot); + + return SVN_NO_ERROR; } @@ -1342,7 +1448,7 @@ prune_deleted(apr_hash_t **entries_pruned, svn_boolean_t hidden; SVN_ERR(svn_wc__entry_is_hidden(&hidden, - svn__apr_hash_index_val(hi))); + apr_hash_this_val(hi))); if (hidden) break; } @@ -1360,8 +1466,8 @@ prune_deleted(apr_hash_t **entries_pruned, hi; hi = apr_hash_next(hi)) { - const void *key = svn__apr_hash_index_key(hi); - const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi); + const void *key = apr_hash_this_key(hi); + const svn_wc_entry_t *entry = apr_hash_this_val(hi); svn_boolean_t hidden; SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry)); @@ -1372,25 +1478,6 @@ prune_deleted(apr_hash_t **entries_pruned, return SVN_NO_ERROR; } -struct entries_read_baton_t -{ - apr_hash_t **new_entries; - svn_wc__db_t *db; - const char *local_abspath; - apr_pool_t *result_pool; -}; - -static svn_error_t * -entries_read_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool) -{ - struct entries_read_baton_t *erb = baton; - - SVN_ERR(read_entries(erb->new_entries, erb->db, erb->local_abspath, - erb->result_pool, scratch_pool)); - - return NULL; -} - svn_error_t * svn_wc__entries_read_internal(apr_hash_t **entries, svn_wc_adm_access_t *adm_access, @@ -1405,21 +1492,9 @@ svn_wc__entries_read_internal(apr_hash_t **entries, svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); const char *local_abspath = svn_wc__adm_access_abspath(adm_access); apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access); - svn_sqlite__db_t *sdb; - struct entries_read_baton_t erb; - - /* ### Use the borrow DB api to handle all calls in a single read - ### transaction. This api is used extensively in our test suite - ### via the entries-read application. */ - SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, db, local_abspath, pool)); - - erb.db = db; - erb.local_abspath = local_abspath; - erb.new_entries = &new_entries; - erb.result_pool = result_pool; - - SVN_ERR(svn_sqlite__with_lock(sdb, entries_read_txn, &erb, pool)); + SVN_ERR(read_entries(&new_entries, db, local_abspath, + result_pool, pool)); svn_wc__adm_access_set_entries(adm_access, new_entries); } @@ -1452,23 +1527,31 @@ insert_node(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + svn_boolean_t present = (node->presence == svn_wc__db_status_normal + || node->presence == svn_wc__db_status_incomplete); SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath); SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnrisnnni", + SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsn", node->wc_id, node->local_relpath, node->op_depth, node->parent_relpath, /* Setting depth for files? */ - svn_depth_to_word(node->depth), - node->changed_rev, - node->changed_date, - node->changed_author, - node->recorded_time)); + (node->kind == svn_node_dir && present) + ? svn_depth_to_word(node->depth) + : NULL)); - if (node->repos_relpath) + if (present && node->repos_relpath) + { + SVN_ERR(svn_sqlite__bind_revnum(stmt, 11, node->changed_rev)); + SVN_ERR(svn_sqlite__bind_int64(stmt, 12, node->changed_date)); + SVN_ERR(svn_sqlite__bind_text(stmt, 13, node->changed_author)); + } + + if (node->repos_relpath + && node->presence != svn_wc__db_status_base_deleted) { SVN_ERR(svn_sqlite__bind_int64(stmt, 5, node->repos_id)); @@ -1477,26 +1560,14 @@ insert_node(svn_sqlite__db_t *sdb, SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision)); } - if (node->presence == svn_wc__db_status_normal) - SVN_ERR(svn_sqlite__bind_text(stmt, 8, "normal")); - else if (node->presence == svn_wc__db_status_not_present) - SVN_ERR(svn_sqlite__bind_text(stmt, 8, "not-present")); - else if (node->presence == svn_wc__db_status_base_deleted) - SVN_ERR(svn_sqlite__bind_text(stmt, 8, "base-deleted")); - else if (node->presence == svn_wc__db_status_incomplete) - SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete")); - else if (node->presence == svn_wc__db_status_excluded) - SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded")); - else if (node->presence == svn_wc__db_status_server_excluded) - SVN_ERR(svn_sqlite__bind_text(stmt, 8, "server-excluded")); + SVN_ERR(svn_sqlite__bind_token(stmt, 8, presence_map, node->presence)); if (node->kind == svn_node_none) SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown")); else - SVN_ERR(svn_sqlite__bind_text(stmt, 10, - svn_node_kind_to_word(node->kind))); + SVN_ERR(svn_sqlite__bind_token(stmt, 10, kind_map, node->kind)); - if (node->kind == svn_node_file) + if (node->kind == svn_node_file && present) { if (!node->checksum && node->op_depth == 0 @@ -1510,19 +1581,25 @@ insert_node(svn_sqlite__db_t *sdb, SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum, scratch_pool)); + + if (node->repos_relpath) + { + if (node->recorded_size != SVN_INVALID_FILESIZE) + SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size)); + + SVN_ERR(svn_sqlite__bind_int64(stmt, 17, node->recorded_time)); + } } - if (node->properties) /* ### Never set, props done later */ + /* ### Never set, props done later */ + if (node->properties && present && node->repos_relpath) SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties, scratch_pool)); - if (node->recorded_size != SVN_INVALID_FILESIZE) - SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size)); - if (node->file_external) SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1)); - if (node->inherited_props) + if (node->inherited_props && present) SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props, scratch_pool)); @@ -1801,6 +1878,10 @@ write_entry(struct write_baton **entry_node, if (entry->copied) { + db_node_t *work = parent_node->work + ? parent_node->work + : parent_node->below_work; + if (entry->copyfrom_url) { working_node->repos_id = repos_id; @@ -1810,20 +1891,37 @@ write_entry(struct write_baton **entry_node, working_node->revision = entry->copyfrom_rev; working_node->op_depth = svn_wc__db_op_depth_for_upgrade(local_relpath); + + if (work && work->repos_relpath + && work->repos_id == repos_id + && work->revision == entry->copyfrom_rev) + { + const char *name; + + name = svn_relpath_skip_ancestor(work->repos_relpath, + working_node->repos_relpath); + + if (name + && !strcmp(name, svn_relpath_basename(local_relpath, NULL))) + { + working_node->op_depth = work->op_depth; + } + } } - else if (parent_node->work && parent_node->work->repos_relpath) + else if (work && work->repos_relpath) { working_node->repos_id = repos_id; working_node->repos_relpath - = svn_relpath_join(parent_node->work->repos_relpath, + = svn_relpath_join(work->repos_relpath, svn_relpath_basename(local_relpath, NULL), result_pool); - working_node->revision = parent_node->work->revision; - working_node->op_depth = parent_node->work->op_depth; + working_node->revision = work->revision; + working_node->op_depth = work->op_depth; } else if (parent_node->below_work && parent_node->below_work->repos_relpath) { + /* Parent deleted, this not-present or similar */ working_node->repos_id = repos_id; working_node->repos_relpath = svn_relpath_join(parent_node->below_work->repos_relpath, @@ -1837,6 +1935,29 @@ write_entry(struct write_baton **entry_node, _("No copyfrom URL for '%s'"), svn_dirent_local_style(local_relpath, scratch_pool)); + + if (work && work->op_depth != working_node->op_depth + && work->repos_relpath + && work->repos_id == working_node->repos_id + && work->presence == svn_wc__db_status_normal + && !below_working_node) + { + /* Introduce a not-present node! */ + below_working_node = MAYBE_ALLOC(below_working_node, scratch_pool); + + below_working_node->wc_id = wc_id; + below_working_node->op_depth = work->op_depth; + below_working_node->local_relpath = local_relpath; + below_working_node->parent_relpath = parent_relpath; + + below_working_node->presence = svn_wc__db_status_not_present; + below_working_node->repos_id = repos_id; + below_working_node->repos_relpath = working_node->local_relpath; + + SVN_ERR(insert_node(sdb, below_working_node, scratch_pool)); + + below_working_node = NULL; /* Don't write a present intermediate! */ + } } if (entry->conflict_old) @@ -1892,7 +2013,7 @@ write_entry(struct write_baton **entry_node, scratch_pool); tree_conflicts = apr_hash_make(result_pool); skel = skel->children; - while(skel) + while (skel) { svn_wc_conflict_description2_t *conflict; svn_skel_t *new_skel; @@ -1909,11 +2030,6 @@ write_entry(struct write_baton **entry_node, WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree); - /* Fix dubious data stored by old clients, local adds don't have - a repository URL. */ - if (conflict->reason == svn_wc_conflict_reason_added) - conflict->src_left_version = NULL; - SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict, scratch_pool, scratch_pool)); @@ -2295,6 +2411,11 @@ write_entry(struct write_baton **entry_node, { working_node->op_depth = parent_node->work->op_depth; } + else if (working_node->presence == svn_wc__db_status_excluded + && parent_node->work) + { + working_node->op_depth = parent_node->work->op_depth; + } else if (!entry->copied) { working_node->op_depth @@ -2358,9 +2479,9 @@ write_actual_only_entries(apr_hash_t *tree_conflicts, actual_node = MAYBE_ALLOC(actual_node, scratch_pool); actual_node->wc_id = wc_id; - actual_node->local_relpath = svn__apr_hash_index_key(hi); + actual_node->local_relpath = apr_hash_this_key(hi); actual_node->parent_relpath = parent_relpath; - actual_node->tree_conflict_data = svn__apr_hash_index_val(hi); + actual_node->tree_conflict_data = apr_hash_this_val(hi); SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node, scratch_pool)); @@ -2418,8 +2539,8 @@ svn_wc__write_upgraded_entries(void **dir_baton, for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - const svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + const svn_wc_entry_t *this_entry = apr_hash_this_val(hi); const char *child_abspath, *child_relpath; svn_wc__text_base_info_t *text_base_info = svn_hash_gets(text_bases_info, name); @@ -2573,8 +2694,8 @@ walker_helper(const char *dirpath, /* Loop over each of the other entries. */ for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - const svn_wc_entry_t *current_entry = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + const svn_wc_entry_t *current_entry = apr_hash_this_val(hi); const char *entrypath; const char *entry_abspath; svn_boolean_t hidden; @@ -2661,11 +2782,11 @@ svn_wc_walk_entries3(const char *path, svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); svn_error_t *err; svn_node_kind_t kind; - svn_depth_t depth; + svn_wc__db_status_t status; SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); - err = svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, &depth, NULL, NULL, + err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -2684,7 +2805,9 @@ svn_wc_walk_entries3(const char *path, walk_baton, pool); } - if (kind == svn_node_file || depth == svn_depth_exclude) + if (kind == svn_node_file + || status == svn_wc__db_status_excluded + || status == svn_wc__db_status_server_excluded) { const svn_wc_entry_t *entry; @@ -2693,27 +2816,24 @@ svn_wc_walk_entries3(const char *path, ### we should not call handle_error for an error the *callback* ### gave us. let it deal with the problem before returning. */ - if (!show_hidden) + if (!show_hidden + && (status == svn_wc__db_status_not_present + || status == svn_wc__db_status_excluded + || status == svn_wc__db_status_server_excluded)) { - svn_boolean_t hidden; - SVN_ERR(svn_wc__db_node_hidden(&hidden, db, local_abspath, pool)); - - if (hidden) - { - /* The fool asked to walk a "hidden" node. Report the node as - unversioned. - - ### this is incorrect behavior. see depth_test 36. the walk - ### API will be revamped to avoid entry structures. we should - ### be able to solve the problem with the new API. (since we - ### shouldn't return a hidden entry here) */ - return walk_callbacks->handle_error( - path, svn_error_createf( - SVN_ERR_UNVERSIONED_RESOURCE, NULL, - _("'%s' is not under version control"), - svn_dirent_local_style(local_abspath, pool)), - walk_baton, pool); - } + /* The fool asked to walk a "hidden" node. Report the node as + unversioned. + + ### this is incorrect behavior. see depth_test 36. the walk + ### API will be revamped to avoid entry structures. we should + ### be able to solve the problem with the new API. (since we + ### shouldn't return a hidden entry here) */ + return walk_callbacks->handle_error( + path, svn_error_createf( + SVN_ERR_UNVERSIONED_RESOURCE, NULL, + _("'%s' is not under version control"), + svn_dirent_local_style(local_abspath, pool)), + walk_baton, pool); } SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE, diff --git a/subversion/libsvn_wc/externals.c b/subversion/libsvn_wc/externals.c index 3725b22..4cbd4f0 100644 --- a/subversion/libsvn_wc/externals.c +++ b/subversion/libsvn_wc/externals.c @@ -68,6 +68,7 @@ * the revision if the revision is found. Set REV_IDX to the index in * LINE_PARTS where the revision specification starts. Remove from * LINE_PARTS the element(s) that specify the revision. + * Set REV_STR to the element that specifies the revision. * PARENT_DIRECTORY_DISPLAY and LINE are given to return a nice error * string. * @@ -76,6 +77,7 @@ */ static svn_error_t * find_and_remove_externals_revision(int *rev_idx, + const char **rev_str, const char **line_parts, int num_line_parts, svn_wc_external_item2_t *item, @@ -137,6 +139,8 @@ find_and_remove_externals_revision(int *rev_idx, line_parts[j] = line_parts[j+shift_count]; line_parts[num_line_parts-shift_count] = NULL; + *rev_str = apr_psprintf(pool, "-r%s", digits_ptr); + /* Found the revision, so leave the function immediately, do * not continue looking for additional revisions. */ return SVN_NO_ERROR; @@ -158,23 +162,29 @@ find_and_remove_externals_revision(int *rev_idx, } svn_error_t * -svn_wc_parse_externals_description3(apr_array_header_t **externals_p, - const char *parent_directory, +svn_wc__parse_externals_description(apr_array_header_t **externals_p, + apr_array_header_t **parser_infos_p, + const char *defining_directory, const char *desc, svn_boolean_t canonicalize_url, apr_pool_t *pool) { int i; apr_array_header_t *externals = NULL; + apr_array_header_t *parser_infos = NULL; apr_array_header_t *lines = svn_cstring_split(desc, "\n\r", TRUE, pool); - const char *parent_directory_display = svn_path_is_url(parent_directory) ? - parent_directory : svn_dirent_local_style(parent_directory, pool); + const char *defining_directory_display = svn_path_is_url(defining_directory) ? + defining_directory : svn_dirent_local_style(defining_directory, pool); /* If an error occurs halfway through parsing, *externals_p should stay * untouched. So, store the list in a local var first. */ if (externals_p) externals = apr_array_make(pool, 1, sizeof(svn_wc_external_item2_t *)); + if (parser_infos_p) + parser_infos = + apr_array_make(pool, 1, sizeof(svn_wc__externals_parser_info_t *)); + for (i = 0; i < lines->nelts; i++) { const char *line = APR_ARRAY_IDX(lines, i, const char *); @@ -186,10 +196,12 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, const char *token1; svn_boolean_t token0_is_url; svn_boolean_t token1_is_url; + svn_wc__externals_parser_info_t *info = NULL; /* Index into line_parts where the revision specification started. */ int rev_idx = -1; + const char *rev_str = NULL; if ((! line) || (line[0] == '#')) continue; @@ -209,6 +221,9 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, item->revision.kind = svn_opt_revision_unspecified; item->peg_revision.kind = svn_opt_revision_unspecified; + if (parser_infos) + info = apr_pcalloc(pool, sizeof(*info)); + /* * There are six different formats of externals: * @@ -231,7 +246,7 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, (SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL, _("Error parsing %s property on '%s': '%s'"), SVN_PROP_EXTERNALS, - parent_directory_display, + defining_directory_display, line); /* To make it easy to check for the forms, find and remove -r N @@ -240,9 +255,10 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, set item->revision to the parsed revision. */ /* ### ugh. stupid cast. */ SVN_ERR(find_and_remove_externals_revision(&rev_idx, + &rev_str, (const char **)line_parts, num_line_parts, item, - parent_directory_display, + defining_directory_display, line, pool)); token0 = line_parts[0]; @@ -258,7 +274,7 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, "cannot use two absolute URLs ('%s' and '%s') in an external; " "one must be a path where an absolute or relative URL is " "checked out to"), - SVN_PROP_EXTERNALS, parent_directory_display, token0, token1); + SVN_PROP_EXTERNALS, defining_directory_display, token0, token1); if (0 == rev_idx && token1_is_url) return svn_error_createf @@ -266,7 +282,7 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, _("Invalid %s property on '%s': " "cannot use a URL '%s' as the target directory for an external " "definition"), - SVN_PROP_EXTERNALS, parent_directory_display, token1); + SVN_PROP_EXTERNALS, defining_directory_display, token1); if (1 == rev_idx && token0_is_url) return svn_error_createf @@ -274,9 +290,9 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, _("Invalid %s property on '%s': " "cannot use a URL '%s' as the target directory for an external " "definition"), - SVN_PROP_EXTERNALS, parent_directory_display, token0); + SVN_PROP_EXTERNALS, defining_directory_display, token0); - /* The appearence of -r N or -rN forces the type of external. + /* The appearance of -r N or -rN forces the type of external. If -r is at the beginning of the line or the first token is an absolute URL or if the second token is not an absolute URL, then the URL supports peg revisions. */ @@ -290,12 +306,34 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, SVN_ERR(svn_opt_parse_path(&item->peg_revision, &item->url, token0, pool)); item->target_dir = token1; + + if (info) + { + info->format = svn_wc__external_description_format_2; + + if (rev_str) + info->rev_str = apr_pstrdup(pool, rev_str); + + if (item->peg_revision.kind != svn_opt_revision_unspecified) + info->peg_rev_str = strrchr(token0, '@'); + } } else { item->target_dir = token0; item->url = token1; item->peg_revision = item->revision; + + if (info) + { + info->format = svn_wc__external_description_format_1; + + if (rev_str) + { + info->rev_str = apr_pstrdup(pool, rev_str); + info->peg_rev_str = info->rev_str; + } + } } SVN_ERR(svn_opt_resolve_revisions(&item->peg_revision, @@ -316,7 +354,7 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, _("Invalid %s property on '%s': " "target '%s' is an absolute path or involves '..'"), SVN_PROP_EXTERNALS, - parent_directory_display, + defining_directory_display, item->target_dir); if (canonicalize_url) @@ -333,15 +371,34 @@ svn_wc_parse_externals_description3(apr_array_header_t **externals_p, if (externals) APR_ARRAY_PUSH(externals, svn_wc_external_item2_t *) = item; + if (parser_infos) + APR_ARRAY_PUSH(parser_infos, svn_wc__externals_parser_info_t *) = info; } if (externals_p) *externals_p = externals; + if (parser_infos_p) + *parser_infos_p = parser_infos; return SVN_NO_ERROR; } svn_error_t * +svn_wc_parse_externals_description3(apr_array_header_t **externals_p, + const char *defining_directory, + const char *desc, + svn_boolean_t canonicalize_url, + apr_pool_t *pool) +{ + return svn_error_trace(svn_wc__parse_externals_description(externals_p, + NULL, + defining_directory, + desc, + canonicalize_url, + pool)); +} + +svn_error_t * svn_wc__externals_find_target_dups(apr_array_header_t **duplicate_targets, apr_array_header_t *externals, apr_pool_t *pool, @@ -405,9 +462,10 @@ struct edit_baton const apr_array_header_t *ext_patterns; const char *diff3cmd; - const char *url; const char *repos_root_url; const char *repos_uuid; + const char *old_repos_relpath; + const char *new_repos_relpath; const char *record_ancestor_abspath; const char *recorded_repos_relpath; @@ -417,8 +475,6 @@ struct edit_baton /* Introducing a new file external */ svn_boolean_t added; - svn_wc_conflict_resolver_func2_t conflict_func; - void *conflict_baton; svn_cancel_func_t cancel_func; void *cancel_baton; svn_wc_notify_func2_t notify_func; @@ -431,7 +487,7 @@ struct edit_baton const svn_checksum_t *original_checksum; /* What we are installing now */ - const char *new_pristine_abspath; + svn_wc__db_install_data_t *install_data; svn_checksum_t *new_sha1_checksum; svn_checksum_t *new_md5_checksum; @@ -517,7 +573,8 @@ open_file(const char *path, *file_baton = eb; SVN_ERR(svn_wc__db_base_get_info(NULL, &kind, &eb->original_revision, - NULL, NULL, NULL, &eb->changed_rev, + &eb->old_repos_relpath, NULL, NULL, + &eb->changed_rev, &eb->changed_date, &eb->changed_author, NULL, &eb->original_checksum, NULL, NULL, &eb->had_props, NULL, NULL, @@ -579,11 +636,12 @@ apply_textdelta(void *file_baton, else src_stream = svn_stream_empty(pool); - SVN_ERR(svn_wc__open_writable_base(&dest_stream, &eb->new_pristine_abspath, - &eb->new_md5_checksum, - &eb->new_sha1_checksum, - eb->db, eb->wri_abspath, - eb->pool, pool)); + SVN_ERR(svn_wc__db_pristine_prepare_install(&dest_stream, + &eb->install_data, + &eb->new_sha1_checksum, + &eb->new_md5_checksum, + eb->db, eb->wri_abspath, + eb->pool, pool)); svn_txdelta_apply(src_stream, dest_stream, NULL, eb->local_abspath, pool, handler, handler_baton); @@ -603,7 +661,7 @@ change_file_prop(void *file_baton, propchange = apr_array_push(eb->propchanges); propchange->name = apr_pstrdup(eb->pool, name); - propchange->value = value ? svn_string_dup(value, eb->pool) : NULL; + propchange->value = svn_string_dup(value, eb->pool); return SVN_NO_ERROR; } @@ -656,11 +714,11 @@ close_file(void *file_baton, behavior to the pristine store. */ if (eb->new_sha1_checksum) { - SVN_ERR(svn_wc__db_pristine_install(eb->db, eb->new_pristine_abspath, + SVN_ERR(svn_wc__db_pristine_install(eb->install_data, eb->new_sha1_checksum, eb->new_md5_checksum, pool)); - eb->new_pristine_abspath = NULL; + eb->install_data = NULL; } /* Merge the changes */ @@ -677,8 +735,6 @@ close_file(void *file_baton, const svn_checksum_t *original_checksum = NULL; svn_boolean_t added = !SVN_IS_VALID_REVNUM(eb->original_revision); - const char *repos_relpath = svn_uri_skip_ancestor(eb->repos_root_url, - eb->url, pool); if (! added) { @@ -767,7 +823,6 @@ close_file(void *file_baton, { svn_node_kind_t disk_kind; svn_boolean_t install_pristine = FALSE; - const char *install_from = NULL; SVN_ERR(svn_io_check_path(eb->local_abspath, &disk_kind, pool)); @@ -832,7 +887,7 @@ close_file(void *file_baton, { SVN_ERR(svn_wc__wq_build_file_install(&work_item, eb->db, eb->local_abspath, - install_from, + NULL, eb->use_commit_times, TRUE, pool, pool)); @@ -853,14 +908,14 @@ close_file(void *file_baton, svn_wc_conflict_version_create2( eb->repos_root_url, eb->repos_uuid, - repos_relpath, + eb->old_repos_relpath, eb->original_revision, svn_node_file, pool), svn_wc_conflict_version_create2( eb->repos_root_url, eb->repos_uuid, - repos_relpath, + eb->new_repos_relpath, *eb->target_revision, svn_node_file, pool), @@ -878,7 +933,7 @@ close_file(void *file_baton, eb->db, eb->local_abspath, eb->wri_abspath, - repos_relpath, + eb->new_repos_relpath, eb->repos_root_url, eb->repos_uuid, *eb->target_revision, @@ -947,6 +1002,7 @@ close_edit(void *edit_baton, if (!eb->file_closed) { + apr_hash_t *wcroot_iprops = NULL; /* The file wasn't updated, but its url or revision might have... e.g. switch between branches for relative externals. @@ -954,53 +1010,26 @@ close_edit(void *edit_baton, investigating when we should and shouldn't update it... and avoid hard to debug edge cases */ - svn_node_kind_t kind; - const char *old_repos_relpath; - svn_revnum_t changed_rev; - apr_time_t changed_date; - const char *changed_author; - const svn_checksum_t *checksum; - apr_hash_t *pristine_props; - const char *repos_relpath = svn_uri_skip_ancestor(eb->repos_root_url, - eb->url, pool); - - SVN_ERR(svn_wc__db_base_get_info(NULL, &kind, NULL, &old_repos_relpath, - NULL, NULL, &changed_rev, &changed_date, - &changed_author, NULL, &checksum, NULL, - NULL, NULL, &pristine_props, NULL, - eb->db, eb->local_abspath, - pool, pool)); + if (eb->iprops) + { + wcroot_iprops = apr_hash_make(pool); + svn_hash_sets(wcroot_iprops, eb->local_abspath, eb->iprops); + } - if (kind != svn_node_file) - return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, - _("Node '%s' is no existing file external"), - svn_dirent_local_style(eb->local_abspath, - pool)); - - SVN_ERR(svn_wc__db_external_add_file( - eb->db, - eb->local_abspath, - eb->wri_abspath, - repos_relpath, - eb->repos_root_url, - eb->repos_uuid, - *eb->target_revision, - pristine_props, - eb->iprops, - eb->changed_rev, - eb->changed_date, - eb->changed_author, - checksum, - NULL /* clear dav props */, - eb->record_ancestor_abspath, - eb->recorded_repos_relpath, - eb->recorded_peg_revision, - eb->recorded_revision, - FALSE, NULL, - TRUE /* keep_recorded_info */, - NULL /* conflict_skel */, - NULL /* work_items */, - pool)); + SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db, + eb->local_abspath, + svn_depth_infinity, + eb->new_repos_relpath, + eb->repos_root_url, + eb->repos_uuid, + *eb->target_revision, + apr_hash_make(pool) + /* exclude_relpaths */, + wcroot_iprops, + TRUE /* empty update */, + eb->notify_func, + eb->notify_baton, + pool)); } return SVN_NO_ERROR; @@ -1024,8 +1053,6 @@ svn_wc__get_file_external_editor(const svn_delta_editor_t **editor, const char *recorded_url, const svn_opt_revision_t *recorded_peg_rev, const svn_opt_revision_t *recorded_rev, - svn_wc_conflict_resolver_func2_t conflict_func, - void *conflict_baton, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -1048,9 +1075,12 @@ svn_wc__get_file_external_editor(const svn_delta_editor_t **editor, eb->name = svn_dirent_basename(eb->local_abspath, NULL); eb->target_revision = target_revision; - eb->url = apr_pstrdup(edit_pool, url); eb->repos_root_url = apr_pstrdup(edit_pool, repos_root_url); eb->repos_uuid = apr_pstrdup(edit_pool, repos_uuid); + eb->new_repos_relpath = svn_uri_skip_ancestor(eb->repos_root_url, url, edit_pool); + eb->old_repos_relpath = eb->new_repos_relpath; + + eb->original_revision = SVN_INVALID_REVNUM; eb->iprops = iprops; @@ -1074,8 +1104,6 @@ svn_wc__get_file_external_editor(const svn_delta_editor_t **editor, else eb->recorded_revision = SVN_INVALID_REVNUM; /* Not fixed/HEAD */ - eb->conflict_func = conflict_func; - eb->conflict_baton = conflict_baton; eb->cancel_func = cancel_func; eb->cancel_baton = cancel_baton; eb->notify_func = notify_func; @@ -1445,10 +1473,8 @@ svn_wc__external_remove(svn_wc_context_t *wc_ctx, else { SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath, - FALSE /* keep_as_working */, - TRUE /* queue_deletes */, - FALSE /* remove_locks */, - SVN_INVALID_REVNUM, + FALSE, TRUE, FALSE, + 0, NULL, NULL, scratch_pool)); SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton, @@ -1591,7 +1617,7 @@ svn_wc__resolve_relative_external_url(const char **resolved_url, apr_pstrndup(scratch_pool, url, num_leading_slashes), svn_relpath_canonicalize(url + num_leading_slashes, scratch_pool), - (char*)NULL); + SVN_VA_NULL); } else { @@ -1698,7 +1724,7 @@ svn_wc__resolve_relative_external_url(const char **resolved_url, SVN_ERR(uri_scheme(&scheme, repos_root_url, scratch_pool)); *resolved_url = svn_uri_canonicalize(apr_pstrcat(scratch_pool, scheme, - ":", url, (char *)NULL), + ":", url, SVN_VA_NULL), result_pool); return SVN_NO_ERROR; } diff --git a/subversion/libsvn_wc/info.c b/subversion/libsvn_wc/info.c index dc80ee7..78e5d7f 100644 --- a/subversion/libsvn_wc/info.c +++ b/subversion/libsvn_wc/info.c @@ -52,7 +52,7 @@ svn_wc_info_dup(const svn_wc_info_t *info, for (i = 0; i < info->conflicts->nelts; i++) { APR_ARRAY_PUSH(new_conflicts, svn_wc_conflict_description2_t *) - = svn_wc__conflict_description2_dup( + = svn_wc_conflict_description2_dup( APR_ARRAY_IDX(info->conflicts, i, const svn_wc_conflict_description2_t *), pool); @@ -141,7 +141,6 @@ build_info_for_node(svn_wc__info2_t **info, { /* Root or child of copy */ tmpinfo->rev = original_revision; - repos_relpath = original_repos_relpath; if (op_root) { @@ -167,34 +166,6 @@ build_info_for_node(svn_wc__info2_t **info, } } } - else if (op_root) - { - /* Local addition */ - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &repos_relpath, - &tmpinfo->repos_root_URL, - &tmpinfo->repos_UUID, - NULL, NULL, NULL, NULL, - db, local_abspath, - result_pool, scratch_pool)); - - if (have_base) - SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &tmpinfo->rev, NULL, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - } - else - { - /* Child of copy. ### Not WC-NG like */ - SVN_ERR(svn_wc__internal_get_origin(NULL, &tmpinfo->rev, - &repos_relpath, - &tmpinfo->repos_root_URL, - &tmpinfo->repos_UUID, NULL, - db, local_abspath, TRUE, - result_pool, scratch_pool)); - } /* ### We should be able to avoid both these calls with the information from read_info() in most cases */ @@ -222,14 +193,20 @@ build_info_for_node(svn_wc__info2_t **info, else wc_info->schedule = svn_wc_schedule_add; } - SVN_ERR(svn_wc__db_read_url(&tmpinfo->URL, db, local_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, + &tmpinfo->repos_root_URL, + &tmpinfo->repos_UUID, + db, local_abspath, + result_pool, scratch_pool)); + + tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL, + repos_relpath, result_pool); } else if (status == svn_wc__db_status_deleted) { - const char *work_del_abspath; + svn_wc__db_status_t w_status; - SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, + SVN_ERR(svn_wc__db_read_pristine_info(&w_status, &tmpinfo->kind, &tmpinfo->last_changed_rev, &tmpinfo->last_changed_date, &tmpinfo->last_changed_author, @@ -239,51 +216,32 @@ build_info_for_node(svn_wc__info2_t **info, db, local_abspath, result_pool, scratch_pool)); + if (w_status == svn_wc__db_status_deleted) + { + /* We have a working not-present status. We don't know anything + about this node, but it *is visible* in STATUS. + + Let's tell that it is excluded */ + + wc_info->depth = svn_depth_exclude; + tmpinfo->kind = svn_node_unknown; + } + /* And now fetch the url and revision of what will be deleted */ SVN_ERR(svn_wc__db_scan_deletion(NULL, &wc_info->moved_to_abspath, - &work_del_abspath, NULL, + NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); - if (work_del_abspath != NULL) - { - /* This is a deletion within a copied subtree. Get the copied-from - * revision. */ - const char *added_abspath = svn_dirent_dirname(work_del_abspath, - scratch_pool); - - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &repos_relpath, - &tmpinfo->repos_root_URL, - &tmpinfo->repos_UUID, - NULL, NULL, NULL, - &tmpinfo->rev, - db, added_abspath, - result_pool, scratch_pool)); - - tmpinfo->URL = svn_path_url_add_component2( - tmpinfo->repos_root_URL, - svn_relpath_join(repos_relpath, - svn_dirent_skip_ancestor(added_abspath, - local_abspath), - scratch_pool), - result_pool); - } - else - { - SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &tmpinfo->rev, - &repos_relpath, - &tmpinfo->repos_root_URL, - &tmpinfo->repos_UUID, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - db, local_abspath, - result_pool, scratch_pool)); - - tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL, - repos_relpath, - result_pool); - } + + SVN_ERR(svn_wc__db_read_repos_info(&tmpinfo->rev, &repos_relpath, + &tmpinfo->repos_root_URL, + &tmpinfo->repos_UUID, + db, local_abspath, + result_pool, scratch_pool)); wc_info->schedule = svn_wc_schedule_delete; + tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL, + repos_relpath, result_pool); } else if (status == svn_wc__db_status_not_present || status == svn_wc__db_status_server_excluded) @@ -291,6 +249,21 @@ build_info_for_node(svn_wc__info2_t **info, *info = NULL; return SVN_NO_ERROR; } + else if (status == svn_wc__db_status_excluded && !repos_relpath) + { + /* We have a WORKING exclude. Avoid segfault on no repos info */ + + SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, + &tmpinfo->repos_root_URL, + &tmpinfo->repos_UUID, + db, local_abspath, + result_pool, scratch_pool)); + + wc_info->schedule = svn_wc_schedule_normal; + tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL, + repos_relpath, result_pool); + tmpinfo->wc_info->depth = svn_depth_exclude; + } else { /* Just a BASE node. We have all the info we need */ @@ -298,10 +271,10 @@ build_info_for_node(svn_wc__info2_t **info, repos_relpath, result_pool); wc_info->schedule = svn_wc_schedule_normal; - } - if (status == svn_wc__db_status_excluded) - tmpinfo->wc_info->depth = svn_depth_exclude; + if (status == svn_wc__db_status_excluded) + wc_info->depth = svn_depth_exclude; + } /* A default */ tmpinfo->size = SVN_INVALID_FILESIZE; @@ -310,9 +283,10 @@ build_info_for_node(svn_wc__info2_t **info, local_abspath, result_pool, scratch_pool)); if (conflicted) - SVN_ERR(svn_wc__read_conflicts(&wc_info->conflicts, db, - local_abspath, - TRUE /* ### create tempfiles */, + SVN_ERR(svn_wc__read_conflicts(&wc_info->conflicts, NULL, + db, local_abspath, + FALSE /* create tempfiles */, + FALSE /* only tree conflicts */, result_pool, scratch_pool)); else wc_info->conflicts = NULL; @@ -373,7 +347,7 @@ struct found_entry_baton svn_boolean_t actual_only; svn_boolean_t first; /* The set of tree conflicts that have been found but not (yet) visited by - * the tree walker. Map of abspath -> svn_wc_conflict_description2_t. */ + * the tree walker. Map of abspath -> empty string. */ apr_hash_t *tree_conflicts; apr_pool_t *pool; }; @@ -533,9 +507,10 @@ svn_wc__get_info(svn_wc_context_t *wc_ctx, for (hi = apr_hash_first(scratch_pool, fe_baton.tree_conflicts); hi; hi = apr_hash_next(hi)) { - const char *this_abspath = svn__apr_hash_index_key(hi); + const char *this_abspath = apr_hash_this_key(hi); const svn_wc_conflict_description2_t *tree_conflict; svn_wc__info2_t *info; + const apr_array_header_t *conflicts; svn_pool_clear(iterpool); @@ -543,35 +518,35 @@ svn_wc__get_info(svn_wc_context_t *wc_ctx, if (!repos_root_url) { - SVN_ERR(svn_wc__internal_get_repos_info(NULL, NULL, - &repos_root_url, - &repos_uuid, - wc_ctx->db, - svn_dirent_dirname( + SVN_ERR(svn_wc__db_read_repos_info(NULL, NULL, + &repos_root_url, + &repos_uuid, + wc_ctx->db, + svn_dirent_dirname( this_abspath, iterpool), - scratch_pool, - iterpool)); + scratch_pool, iterpool)); } info->repos_root_URL = repos_root_url; info->repos_UUID = repos_uuid; - SVN_ERR(svn_wc__read_conflicts(&info->wc_info->conflicts, + SVN_ERR(svn_wc__read_conflicts(&conflicts, NULL, wc_ctx->db, this_abspath, - TRUE /* ### create tempfiles */, + FALSE /* create tempfiles */, + FALSE /* only tree conflicts */, iterpool, iterpool)); - - if (! info->wc_info->conflicts || ! info->wc_info->conflicts->nelts) + if (! conflicts || ! conflicts->nelts) continue; - tree_conflict = APR_ARRAY_IDX(info->wc_info->conflicts, 0, - svn_wc_conflict_description2_t *); + tree_conflict = APR_ARRAY_IDX(conflicts, 0, + const svn_wc_conflict_description2_t *); if (!depth_includes(local_abspath, depth, tree_conflict->local_abspath, tree_conflict->node_kind, iterpool)) continue; + info->wc_info->conflicts = conflicts; SVN_ERR(receiver(receiver_baton, this_abspath, info, iterpool)); } svn_pool_destroy(iterpool); diff --git a/subversion/libsvn_wc/libsvn_wc.pc.in b/subversion/libsvn_wc/libsvn_wc.pc.in new file mode 100644 index 0000000..f44387e --- /dev/null +++ b/subversion/libsvn_wc/libsvn_wc.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_wc +Description: Subversion Working Copy Library +Version: @PACKAGE_VERSION@ +Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private: libsvn_delta libsvn_diff libsvn_subr +Libs: -L${libdir} -lsvn_wc +Cflags: -I${includedir} diff --git a/subversion/libsvn_wc/lock.c b/subversion/libsvn_wc/lock.c index 36fbb0e..2392c3f 100644 --- a/subversion/libsvn_wc/lock.c +++ b/subversion/libsvn_wc/lock.c @@ -1211,7 +1211,7 @@ open_anchor(svn_wc_adm_access_t **anchor_access, if (disjoint) { /* Switched or disjoint, so drop P_ACCESS. Don't close any - descendents, or we might blast the child. */ + descendants, or we might blast the child. */ err = close_single(p_access, FALSE /* preserve_lock */, pool); if (err) { @@ -1308,13 +1308,13 @@ do_close(svn_wc_adm_access_t *adm_access, /* Gather all the opened access batons from the DB. */ opened = svn_wc__db_temp_get_all_access(adm_access->db, scratch_pool); - /* Close any that are descendents of this baton. */ + /* Close any that are descendants of this baton. */ for (hi = apr_hash_first(scratch_pool, opened); hi; hi = apr_hash_next(hi)) { - const char *abspath = svn__apr_hash_index_key(hi); - svn_wc_adm_access_t *child = svn__apr_hash_index_val(hi); + const char *abspath = apr_hash_this_key(hi); + svn_wc_adm_access_t *child = apr_hash_this_val(hi); const char *path = child->path; if (IS_MISSING(child)) @@ -1630,7 +1630,7 @@ svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath, scratch_pool, scratch_pool)); /* It's possible for the required lock path to be an ancestor - of, a descendent of, or equal to, the obtained lock path. If + of, a descendant of, or equal to, the obtained lock path. If it's an ancestor we have to try again, otherwise the obtained lock will do. */ child = svn_dirent_skip_ancestor(required_abspath, obtained_abspath); @@ -1643,7 +1643,7 @@ svn_wc__acquire_write_lock_for_resolve(const char **lock_root_abspath, } else { - /* required should be a descendent of, or equal to, obtained */ + /* required should be a descendant of, or equal to, obtained */ SVN_ERR_ASSERT(!strcmp(required_abspath, obtained_abspath) || svn_dirent_skip_ancestor(obtained_abspath, required_abspath)); diff --git a/subversion/libsvn_wc/merge.c b/subversion/libsvn_wc/merge.c index 7cff3e4..85ec632 100644 --- a/subversion/libsvn_wc/merge.c +++ b/subversion/libsvn_wc/merge.c @@ -26,10 +26,11 @@ #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_pools.h" +#include "svn_props.h" #include "wc.h" -#include "adm_files.h" #include "conflicts.h" +#include "props.h" #include "translate.h" #include "workqueue.h" @@ -179,7 +180,7 @@ detranslate_wc_file(const char **detranslated_abspath, = prop ? (prop->value ? prop->value->data : NULL) : old_mime_value; old_is_binary = old_mime_value && svn_mime_type_is_binary(old_mime_value); - new_is_binary = new_mime_value && svn_mime_type_is_binary(new_mime_value);; + new_is_binary = new_mime_value && svn_mime_type_is_binary(new_mime_value); } /* See what translations we want to do */ @@ -391,6 +392,8 @@ do_text_merge(svn_boolean_t *contains_conflicts, const char *target_label, const char *left_label, const char *right_label, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *pool) { svn_diff_t *diff; @@ -415,13 +418,14 @@ do_text_merge(svn_boolean_t *contains_conflicts, ostream = svn_stream_from_aprfile2(result_f, TRUE, pool); - SVN_ERR(svn_diff_file_output_merge2(ostream, diff, + SVN_ERR(svn_diff_file_output_merge3(ostream, diff, left, detranslated_target, right, left_marker, target_marker, right_marker, "=======", /* separator */ - svn_diff_conflict_display_modified_latest, + svn_diff_conflict_display_modified_original_latest, + cancel_func, cancel_baton, pool)); SVN_ERR(svn_stream_close(ostream)); @@ -725,16 +729,20 @@ merge_file_trivial(svn_skel_t **work_items, { svn_stream_t *tmp_src; svn_stream_t *tmp_dst; + const char *tmp_dir; SVN_ERR(svn_stream_open_readonly(&tmp_src, right_abspath, scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__open_writable_base(&tmp_dst, &right_abspath, - NULL, NULL, - db, target_abspath, - scratch_pool, - scratch_pool)); + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir, db, + target_abspath, + scratch_pool, + scratch_pool)); + + SVN_ERR(svn_stream_open_unique(&tmp_dst, &right_abspath, + tmp_dir, svn_io_file_del_none, + scratch_pool, scratch_pool)); SVN_ERR(svn_stream_copy3(tmp_src, tmp_dst, cancel_func, cancel_baton, @@ -866,15 +874,17 @@ merge_text_file(svn_skel_t **work_items, target_label, left_label, right_label, + cancel_func, cancel_baton, pool)); SVN_ERR(svn_io_file_close(result_f, pool)); /* Determine the MERGE_OUTCOME, and record any conflict. */ - if (contains_conflicts && ! dry_run) + if (contains_conflicts) { *merge_outcome = svn_wc_merge_conflict; - if (*merge_outcome == svn_wc_merge_conflict) + + if (! dry_run) { const char *left_copy, *right_copy, *target_copy; @@ -902,12 +912,7 @@ merge_text_file(svn_skel_t **work_items, result_pool, scratch_pool)); } - - if (*merge_outcome == svn_wc_merge_merged) - goto done; } - else if (contains_conflicts && dry_run) - *merge_outcome = svn_wc_merge_conflict; else { svn_boolean_t same, special; @@ -941,7 +946,6 @@ merge_text_file(svn_skel_t **work_items, *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); } -done: /* Remove the tempfile after use */ SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, mt->local_abspath, result_target, @@ -1226,6 +1230,7 @@ svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, apr_hash_t *pristine_props = NULL; apr_hash_t *old_actual_props; apr_hash_t *new_actual_props = NULL; + svn_node_kind_t kind; SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath)); SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath)); @@ -1238,7 +1243,6 @@ svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, /* Sanity check: the merge target must be a file under revision control */ { svn_wc__db_status_t status; - svn_node_kind_t kind; svn_boolean_t had_props; svn_boolean_t props_mod; svn_boolean_t conflicted; @@ -1401,7 +1405,7 @@ svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome, svn_boolean_t text_conflicted, prop_conflicted; SVN_ERR(svn_wc__conflict_invoke_resolver( - wc_ctx->db, target_abspath, + wc_ctx->db, target_abspath, kind, conflict_skel, merge_options, conflict_func, conflict_baton, cancel_func, cancel_baton, diff --git a/subversion/libsvn_wc/node.c b/subversion/libsvn_wc/node.c index a1d6b02..560a899 100644 --- a/subversion/libsvn_wc/node.c +++ b/subversion/libsvn_wc/node.c @@ -54,18 +54,14 @@ /* Set *CHILDREN_ABSPATHS to a new array of the full paths formed by joining - * each name in REL_CHILDREN onto DIR_ABSPATH. If SHOW_HIDDEN is false then - * omit any paths that are reported as 'hidden' by svn_wc__db_node_hidden(). + * each name in REL_CHILDREN onto DIR_ABSPATH. * * Allocate the output array and its elements in RESULT_POOL. */ -static svn_error_t * -filter_and_make_absolute(const apr_array_header_t **children_abspaths, - svn_wc_context_t *wc_ctx, - const char *dir_abspath, - const apr_array_header_t *rel_children, - svn_boolean_t show_hidden, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +static void +make_absolute(const apr_array_header_t **children_abspaths, + const char *dir_abspath, + const apr_array_header_t *rel_children, + apr_pool_t *result_pool) { apr_array_header_t *children; int i; @@ -74,29 +70,13 @@ filter_and_make_absolute(const apr_array_header_t **children_abspaths, sizeof(const char *)); for (i = 0; i < rel_children->nelts; i++) { - const char *child_abspath = svn_dirent_join(dir_abspath, - APR_ARRAY_IDX(rel_children, - i, - const char *), - result_pool); - - /* Don't add hidden nodes to *CHILDREN if we don't want them. */ - if (!show_hidden) - { - svn_boolean_t child_is_hidden; - - SVN_ERR(svn_wc__db_node_hidden(&child_is_hidden, wc_ctx->db, - child_abspath, scratch_pool)); - if (child_is_hidden) - continue; - } - - APR_ARRAY_PUSH(children, const char *) = child_abspath; + const char *name = APR_ARRAY_IDX(rel_children, i, const char *); + APR_ARRAY_PUSH(children, const char *) = + svn_dirent_join(dir_abspath, name, + result_pool); } *children_abspaths = children; - - return SVN_NO_ERROR; } @@ -104,139 +84,36 @@ svn_error_t * svn_wc__node_get_children_of_working_node(const apr_array_header_t **children, svn_wc_context_t *wc_ctx, const char *dir_abspath, - svn_boolean_t show_hidden, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - const apr_array_header_t *rel_children; + const apr_array_header_t *child_names; - SVN_ERR(svn_wc__db_read_children_of_working_node(&rel_children, + SVN_ERR(svn_wc__db_read_children_of_working_node(&child_names, wc_ctx->db, dir_abspath, scratch_pool, scratch_pool)); - SVN_ERR(filter_and_make_absolute(children, wc_ctx, dir_abspath, - rel_children, show_hidden, - result_pool, scratch_pool)); + make_absolute(children, dir_abspath, child_names, result_pool); return SVN_NO_ERROR; } - -svn_error_t * -svn_wc__node_get_children(const apr_array_header_t **children, - svn_wc_context_t *wc_ctx, - const char *dir_abspath, - svn_boolean_t show_hidden, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const apr_array_header_t *rel_children; - - SVN_ERR(svn_wc__db_read_children(&rel_children, wc_ctx->db, dir_abspath, - scratch_pool, scratch_pool)); - SVN_ERR(filter_and_make_absolute(children, wc_ctx, dir_abspath, - rel_children, show_hidden, - result_pool, scratch_pool)); - return SVN_NO_ERROR; -} - svn_error_t * -svn_wc__internal_get_repos_info(svn_revnum_t *revision, - const char **repos_relpath, - const char **repos_root_url, - const char **repos_uuid, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +svn_wc__node_get_not_present_children(const apr_array_header_t **children, + svn_wc_context_t *wc_ctx, + const char *dir_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_wc__db_status_t status; - svn_boolean_t have_work; - - SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, repos_relpath, - repos_root_url, repos_uuid, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, &have_work, - db, local_abspath, - result_pool, scratch_pool)); - - if ((repos_relpath ? *repos_relpath != NULL : TRUE) - && (repos_root_url ? *repos_root_url != NULL: TRUE) - && (repos_uuid ? *repos_uuid != NULL : TRUE)) - return SVN_NO_ERROR; /* We got the requested information */ - - if (!have_work) /* not-present, (server-)excluded? */ - { - return SVN_NO_ERROR; /* Can't fetch more */ - } - - if (status == svn_wc__db_status_deleted) - { - const char *base_del_abspath, *wrk_del_abspath; - - SVN_ERR(svn_wc__db_scan_deletion(&base_del_abspath, NULL, - &wrk_del_abspath, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - - if (base_del_abspath) - { - SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, repos_relpath, - repos_root_url, repos_uuid, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, - db, base_del_abspath, - result_pool, scratch_pool)); - - /* If we have a repos_relpath, it is of the op-root */ - if (repos_relpath) - *repos_relpath = svn_relpath_join(*repos_relpath, - svn_dirent_skip_ancestor(base_del_abspath, - local_abspath), - result_pool); - /* We keep revision as SVN_INVALID_REVNUM */ - } - else if (wrk_del_abspath) - { - const char *op_root_abspath = NULL; - - SVN_ERR(svn_wc__db_scan_addition(NULL, repos_relpath - ? &op_root_abspath : NULL, - repos_relpath, repos_root_url, - repos_uuid, NULL, NULL, NULL, NULL, - db, svn_dirent_dirname( - wrk_del_abspath, - scratch_pool), - result_pool, scratch_pool)); - - /* If we have a repos_relpath, it is of the op-root */ - if (repos_relpath) - *repos_relpath = svn_relpath_join( - *repos_relpath, - svn_dirent_skip_ancestor(op_root_abspath, - local_abspath), - result_pool); - } - } - else /* added, or WORKING incomplete */ - { - const char *op_root_abspath = NULL; - - /* We have an addition. scan_addition() will find the intended - repository location by scanning up the tree. */ - SVN_ERR(svn_wc__db_scan_addition(NULL, repos_relpath - ? &op_root_abspath : NULL, - repos_relpath, repos_root_url, - repos_uuid, NULL, NULL, NULL, NULL, - db, local_abspath, - result_pool, scratch_pool)); - } + const apr_array_header_t *child_names; - SVN_ERR_ASSERT(repos_root_url == NULL || *repos_root_url != NULL); - SVN_ERR_ASSERT(repos_uuid == NULL || *repos_uuid != NULL); + SVN_ERR(svn_wc__db_base_read_not_present_children( + &child_names, + wc_ctx->db, dir_abspath, + scratch_pool, scratch_pool)); + make_absolute(children, dir_abspath, child_names, result_pool); return SVN_NO_ERROR; } + svn_error_t * svn_wc__node_get_repos_info(svn_revnum_t *revision, const char **repos_relpath, @@ -248,12 +125,12 @@ svn_wc__node_get_repos_info(svn_revnum_t *revision, apr_pool_t *scratch_pool) { return svn_error_trace( - svn_wc__internal_get_repos_info(revision, - repos_relpath, - repos_root_url, - repos_uuid, - wc_ctx->db, local_abspath, - result_pool, scratch_pool)); + svn_wc__db_read_repos_info(revision, + repos_relpath, + repos_root_url, + repos_uuid, + wc_ctx->db, local_abspath, + result_pool, scratch_pool)); } /* Convert DB_KIND into the appropriate NODE_KIND value. @@ -323,21 +200,6 @@ svn_wc_read_kind2(svn_node_kind_t *kind, } svn_error_t * -svn_wc__node_get_depth(svn_depth_t *depth, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - return svn_error_trace( - svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, depth, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - wc_ctx->db, local_abspath, scratch_pool, - scratch_pool)); -} - -svn_error_t * svn_wc__node_get_changed_info(svn_revnum_t *changed_rev, apr_time_t *changed_date, const char **changed_author, @@ -362,8 +224,18 @@ svn_wc__node_get_url(const char **url, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - return svn_error_trace(svn_wc__db_read_url(url, wc_ctx->db, local_abspath, - result_pool, scratch_pool)); + const char *repos_root_url; + const char *repos_relpath; + + SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, &repos_root_url, + NULL, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + + *url = svn_path_url_add_component2(repos_root_url, repos_relpath, + result_pool); + + return SVN_NO_ERROR; } /* A recursive node-walker, helper for svn_wc__internal_walk_children(). @@ -388,25 +260,24 @@ walker_helper(svn_wc__db_t *db, void *cancel_baton, apr_pool_t *scratch_pool) { - apr_hash_t *rel_children_info; - apr_hash_index_t *hi; apr_pool_t *iterpool; + const apr_array_header_t *items; + int i; if (depth == svn_depth_empty) return SVN_NO_ERROR; - SVN_ERR(svn_wc__db_read_children_walker_info(&rel_children_info, db, - dir_abspath, scratch_pool, - scratch_pool)); + iterpool = svn_pool_create(scratch_pool); + SVN_ERR(svn_wc__db_read_children_walker_info(&items, db, + dir_abspath, scratch_pool, + iterpool)); - iterpool = svn_pool_create(scratch_pool); - for (hi = apr_hash_first(scratch_pool, rel_children_info); - hi; - hi = apr_hash_next(hi)) + for (i = 0; i < items->nelts; i++) { - const char *child_name = svn__apr_hash_index_key(hi); - struct svn_wc__db_walker_info_t *wi = svn__apr_hash_index_val(hi); + struct svn_wc__db_walker_info_t *wi = + APR_ARRAY_IDX(items, i, struct svn_wc__db_walker_info_t *); + const char *child_name = wi->name; svn_node_kind_t child_kind = wi->kind; svn_wc__db_status_t child_status = wi->status; const char *child_abspath; @@ -489,6 +360,7 @@ svn_wc__internal_walk_children(svn_wc__db_t *db, svn_node_kind_t kind; svn_wc__db_status_t status; apr_hash_t *changelist_hash = NULL; + const char *changelist = NULL; SVN_ERR_ASSERT(walk_depth >= svn_depth_empty && walk_depth <= svn_depth_infinity); @@ -501,14 +373,17 @@ svn_wc__internal_walk_children(svn_wc__db_t *db, SVN_ERR(svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + changelist_hash ? &changelist : NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(convert_db_kind_to_node_kind(&kind, db_kind, status, show_hidden)); - if (svn_wc__internal_changelist_match(db, local_abspath, - changelist_hash, scratch_pool)) - SVN_ERR(walk_callback(local_abspath, kind, walk_baton, scratch_pool)); + if (!changelist_hash + || (changelist && svn_hash_gets(changelist_hash, changelist))) + { + SVN_ERR(walk_callback(local_abspath, kind, walk_baton, scratch_pool)); + } if (db_kind == svn_node_file || status == svn_wc__db_status_not_present @@ -531,54 +406,6 @@ svn_wc__internal_walk_children(svn_wc__db_t *db, } svn_error_t * -svn_wc__node_is_status_deleted(svn_boolean_t *is_deleted, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - svn_wc__db_status_t status; - - SVN_ERR(svn_wc__db_read_info(&status, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - wc_ctx->db, local_abspath, - scratch_pool, scratch_pool)); - - *is_deleted = (status == svn_wc__db_status_deleted); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_wc__node_get_deleted_ancestor(const char **deleted_ancestor_abspath, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_wc__db_status_t status; - - *deleted_ancestor_abspath = NULL; - - SVN_ERR(svn_wc__db_read_info(&status, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - wc_ctx->db, local_abspath, - scratch_pool, scratch_pool)); - - if (status == svn_wc__db_status_deleted) - SVN_ERR(svn_wc__db_scan_deletion(deleted_ancestor_abspath, NULL, NULL, - NULL, wc_ctx->db, local_abspath, - result_pool, scratch_pool)); - - return SVN_NO_ERROR; -} - -svn_error_t * svn_wc__node_is_not_present(svn_boolean_t *is_not_present, svn_boolean_t *is_excluded, svn_boolean_t *is_server_excluded, @@ -671,7 +498,6 @@ svn_wc__node_get_base(svn_node_kind_t *kind, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t ignore_enoent, - svn_boolean_t show_hidden, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -691,9 +517,8 @@ svn_wc__node_get_base(svn_node_kind_t *kind, if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); else if (err - || (!err && !show_hidden - && (status != svn_wc__db_status_normal - && status != svn_wc__db_status_incomplete))) + || (status != svn_wc__db_status_normal + && status != svn_wc__db_status_incomplete)) { if (!ignore_enoent) { @@ -783,133 +608,6 @@ svn_wc__node_get_pre_ng_status_data(svn_revnum_t *revision, } svn_error_t * -svn_wc__internal_node_get_schedule(svn_wc_schedule_t *schedule, - svn_boolean_t *copied, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - svn_wc__db_status_t status; - svn_boolean_t op_root; - svn_boolean_t have_base; - svn_boolean_t have_work; - svn_boolean_t have_more_work; - const char *copyfrom_relpath; - - if (schedule) - *schedule = svn_wc_schedule_normal; - if (copied) - *copied = FALSE; - - SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, ©from_relpath, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &op_root, NULL, NULL, - &have_base, &have_more_work, &have_work, - db, local_abspath, scratch_pool, scratch_pool)); - - switch (status) - { - case svn_wc__db_status_not_present: - case svn_wc__db_status_server_excluded: - case svn_wc__db_status_excluded: - /* We used status normal in the entries world. */ - if (schedule) - *schedule = svn_wc_schedule_normal; - break; - case svn_wc__db_status_normal: - case svn_wc__db_status_incomplete: - break; - - case svn_wc__db_status_deleted: - { - if (schedule) - *schedule = svn_wc_schedule_delete; - - if (!copied) - break; - - if (have_more_work || !have_base) - *copied = TRUE; - else - { - const char *work_del_abspath; - - /* Find out details of our deletion. */ - SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, - &work_del_abspath, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - - if (work_del_abspath) - *copied = TRUE; /* Working deletion */ - } - break; - } - case svn_wc__db_status_added: - { - if (!op_root) - { - if (copied) - *copied = TRUE; - - if (schedule) - *schedule = svn_wc_schedule_normal; - - break; - } - - if (copied) - *copied = (copyfrom_relpath != NULL); - - if (schedule) - *schedule = svn_wc_schedule_add; - else - break; - - /* Check for replaced */ - if (have_base || have_more_work) - { - svn_wc__db_status_t below_working; - SVN_ERR(svn_wc__db_info_below_working(&have_base, &have_work, - &below_working, - db, local_abspath, - scratch_pool)); - - /* If the node is not present or deleted (read: not present - in working), then the node is not a replacement */ - if (below_working != svn_wc__db_status_not_present - && below_working != svn_wc__db_status_deleted) - { - *schedule = svn_wc_schedule_replace; - break; - } - } - break; - } - default: - SVN_ERR_MALFUNCTION(); - } - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_wc__node_get_schedule(svn_wc_schedule_t *schedule, - svn_boolean_t *copied, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - return svn_error_trace( - svn_wc__internal_node_get_schedule(schedule, - copied, - wc_ctx->db, - local_abspath, - scratch_pool)); -} - -svn_error_t * svn_wc__node_clear_dav_cache_recursive(svn_wc_context_t *wc_ctx, const char *local_abspath, apr_pool_t *scratch_pool) @@ -952,6 +650,7 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, + svn_depth_t *depth, const char **copy_root_abspath, svn_wc__db_t *db, const char *local_abspath, @@ -964,20 +663,24 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, const char *original_repos_uuid; svn_revnum_t original_revision; svn_wc__db_status_t status; + svn_boolean_t have_more_work; + svn_boolean_t op_root; const char *tmp_repos_relpath; + if (copy_root_abspath) + *copy_root_abspath = NULL; if (!repos_relpath) repos_relpath = &tmp_repos_relpath; SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, repos_relpath, repos_root_url, repos_uuid, NULL, NULL, NULL, - NULL, NULL, NULL, + depth, NULL, NULL, &original_repos_relpath, &original_repos_root_url, &original_repos_uuid, &original_revision, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, is_copy, + NULL, NULL, NULL, NULL, NULL, &op_root, NULL, + NULL, NULL, &have_more_work, is_copy, db, local_abspath, result_pool, scratch_pool)); if (*repos_relpath) @@ -995,6 +698,7 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, if (original_repos_relpath) { + /* We an have a copy */ *repos_relpath = original_repos_relpath; if (revision) *revision = original_revision; @@ -1005,21 +709,19 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, if (copy_root_abspath == NULL) return SVN_NO_ERROR; + else if (op_root) + { + *copy_root_abspath = apr_pstrdup(result_pool, local_abspath); + return SVN_NO_ERROR; + } } { svn_boolean_t scan_working = FALSE; - if (status == svn_wc__db_status_added) + if (status == svn_wc__db_status_added + || (status == svn_wc__db_status_deleted && have_more_work)) scan_working = TRUE; - else if (status == svn_wc__db_status_deleted) - { - svn_boolean_t have_base; - /* Is this a BASE or a WORKING delete? */ - SVN_ERR(svn_wc__db_info_below_working(&have_base, &scan_working, - &status, db, local_abspath, - scratch_pool)); - } if (scan_working) { @@ -1079,6 +781,7 @@ svn_wc__node_get_origin(svn_boolean_t *is_copy, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, + svn_depth_t *depth, const char **copy_root_abspath, svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -1088,7 +791,7 @@ svn_wc__node_get_origin(svn_boolean_t *is_copy, { return svn_error_trace(svn_wc__internal_get_origin(is_copy, revision, repos_relpath, repos_root_url, repos_uuid, - copy_root_abspath, + depth, copy_root_abspath, wc_ctx->db, local_abspath, scan_deleted, result_pool, scratch_pool)); } @@ -1364,16 +1067,22 @@ svn_wc__node_was_moved_away(const char **moved_to_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_boolean_t is_deleted; + svn_wc__db_status_t status; if (moved_to_abspath) *moved_to_abspath = NULL; if (op_root_abspath) *op_root_abspath = NULL; - SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, wc_ctx, local_abspath, - scratch_pool)); - if (is_deleted) + SVN_ERR(svn_wc__db_read_info(&status, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_deleted) SVN_ERR(svn_wc__db_scan_deletion(NULL, moved_to_abspath, NULL, op_root_abspath, wc_ctx->db, local_abspath, diff --git a/subversion/libsvn_wc/old-and-busted.c b/subversion/libsvn_wc/old-and-busted.c index b87be85..8cd94af 100644 --- a/subversion/libsvn_wc/old-and-busted.c +++ b/subversion/libsvn_wc/old-and-busted.c @@ -443,7 +443,7 @@ svn_wc__serialize_file_external(const char **str, SVN_ERR(opt_revision_to_string(&s1, path, peg_rev, pool)); SVN_ERR(opt_revision_to_string(&s2, path, rev, pool)); - s = apr_pstrcat(pool, s1, ":", s2, ":", path, (char *)NULL); + s = apr_pstrcat(pool, s1, ":", s2, ":", path, SVN_VA_NULL); } else s = NULL; @@ -1154,7 +1154,7 @@ resolve_to_defaults(apr_hash_t *entries, /* Then use it to fill in missing information in other entries. */ for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { - svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi); + svn_wc_entry_t *this_entry = apr_hash_this_val(hi); if (this_entry == default_entry) /* THIS_DIR already has all the information it can possibly diff --git a/subversion/libsvn_wc/props.c b/subversion/libsvn_wc/props.c index a7b2339..664bcf1 100644 --- a/subversion/libsvn_wc/props.c +++ b/subversion/libsvn_wc/props.c @@ -62,31 +62,6 @@ #include "svn_private_config.h" -/* Forward declaration. */ -static svn_error_t * -prop_conflict_from_skel(const svn_string_t **conflict_desc, - const svn_skel_t *skel, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - -/* Given a *SINGLE* property conflict in PROP_SKEL, generate a description - for it, and write it to STREAM, along with a trailing EOL sequence. - - See prop_conflict_from_skel() for details on PROP_SKEL. */ -static svn_error_t * -append_prop_conflict(svn_stream_t *stream, - const svn_skel_t *prop_skel, - apr_pool_t *pool) -{ - /* TODO: someday, perhaps prefix each conflict_description with a - timestamp or something? */ - const svn_string_t *conflict_desc; - - SVN_ERR(prop_conflict_from_skel(&conflict_desc, prop_skel, pool, pool)); - - return svn_stream_puts(stream, conflict_desc->data); -} - /*---------------------------------------------------------------------*/ /*** Merging propchanges into the working copy ***/ @@ -352,7 +327,8 @@ svn_wc_merge_props3(svn_wc_notify_state_t *state, { svn_boolean_t prop_conflicted; - SVN_ERR(svn_wc__conflict_invoke_resolver(db, local_abspath, conflict_skel, + SVN_ERR(svn_wc__conflict_invoke_resolver(db, local_abspath, kind, + conflict_skel, NULL /* merge_options */, conflict_func, conflict_baton, cancel_func, cancel_baton, @@ -531,89 +507,96 @@ maybe_prop_value(const svn_skel_t *skel, } -/* Parse a property conflict description from the provided SKEL. - The result includes a descriptive message (see generate_conflict_message) - and maybe a diff of property values containing conflict markers. - The result will be allocated in RESULT_POOL. - - Note: SKEL is a single property conflict of the form: - - ("prop" ([ORIGINAL]) ([MINE]) ([INCOMING]) ([INCOMING_BASE])) - - See notes/wc-ng/conflict-storage for more information. */ +/* Create a property rejection description for the specified property. + The result will be allocated in RESULT_POOL. */ static svn_error_t * -prop_conflict_from_skel(const svn_string_t **conflict_desc, - const svn_skel_t *skel, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +prop_conflict_new(const svn_string_t **conflict_desc, + const char *propname, + const svn_string_t *original, + const svn_string_t *mine, + const svn_string_t *incoming, + const svn_string_t *incoming_base, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - const svn_string_t *original; - const svn_string_t *mine; - const svn_string_t *incoming; - const svn_string_t *incoming_base; - const char *propname; svn_diff_t *diff; svn_diff_file_options_t *diff_opts; svn_stringbuf_t *buf; - svn_boolean_t original_is_binary; + svn_boolean_t incoming_base_is_binary; svn_boolean_t mine_is_binary; svn_boolean_t incoming_is_binary; - /* Navigate to the property name. */ - skel = skel->children->next; - - /* We need to copy these into SCRATCH_POOL in order to nul-terminate - the values. */ - propname = apr_pstrmemdup(scratch_pool, skel->data, skel->len); - original = maybe_prop_value(skel->next, scratch_pool); - mine = maybe_prop_value(skel->next->next, scratch_pool); - incoming = maybe_prop_value(skel->next->next->next, scratch_pool); - incoming_base = maybe_prop_value(skel->next->next->next->next, scratch_pool); - buf = generate_conflict_message(propname, original, mine, incoming, incoming_base, scratch_pool); + /* Convert deleted or not-yet-added values to empty-string values, for the + purposes of diff generation and binary detection. */ if (mine == NULL) mine = svn_string_create_empty(scratch_pool); if (incoming == NULL) incoming = svn_string_create_empty(scratch_pool); + if (incoming_base == NULL) + incoming_base = svn_string_create_empty(scratch_pool); - /* Pick a suitable base for the conflict diff. - * The incoming value is always a change, - * but the local value might not have changed. */ - if (original == NULL) - { - if (incoming_base) - original = incoming_base; - else - original = svn_string_create_empty(scratch_pool); - } - else if (incoming_base && svn_string_compare(original, mine)) - original = incoming_base; + /* How we render the conflict: + + We have four sides: original, mine, incoming_base, incoming. + We render the conflict as a 3-way diff. A diff3 API has three parts, + called: + + <<< - original + ||| - modified (or "older") + === - latest (or "theirs") + >>> + + We fill those parts as follows: + + PART FILLED BY SKEL MEMBER USER-FACING ROLE + ==== ===================== ================ + original mine was WORKING tree at conflict creation + modified incoming_base left-hand side of merge + latest incoming right-hand side of merge + (none) original was BASE tree at conflict creation + + An 'update -r rN' is treated like a 'merge -r BASE:rN', i.e., in an + 'update' operation skel->original and skel->incoming_base coincide. + + Note that the term "original" is used both in the skel and in diff3 + with different meanings. Note also that the skel's ORIGINAL value was + at some point in the BASE tree, but the BASE tree need not have contained + the INCOMING_BASE value. + + Yes, it's confusing. */ /* If any of the property values involved in the diff is binary data, * do not generate a diff. */ - original_is_binary = svn_io_is_binary_data(original->data, original->len); + incoming_base_is_binary = svn_io_is_binary_data(incoming_base->data, + incoming_base->len); mine_is_binary = svn_io_is_binary_data(mine->data, mine->len); incoming_is_binary = svn_io_is_binary_data(incoming->data, incoming->len); - if (!(original_is_binary || mine_is_binary || incoming_is_binary)) + if (!(incoming_base_is_binary || mine_is_binary || incoming_is_binary)) { diff_opts = svn_diff_file_options_create(scratch_pool); diff_opts->ignore_space = svn_diff_file_ignore_space_none; diff_opts->ignore_eol_style = FALSE; diff_opts->show_c_function = FALSE; - SVN_ERR(svn_diff_mem_string_diff3(&diff, original, mine, incoming, + /* Pass skel member INCOMING_BASE into the formal parameter ORIGINAL. + Ignore the skel member ORIGINAL. */ + SVN_ERR(svn_diff_mem_string_diff3(&diff, incoming_base, mine, incoming, diff_opts, scratch_pool)); if (svn_diff_contains_conflicts(diff)) { svn_stream_t *stream; svn_diff_conflict_display_style_t style; const char *mine_marker = _("<<<<<<< (local property value)"); - const char *incoming_marker = _(">>>>>>> (incoming property value)"); + const char *incoming_marker = _(">>>>>>> (incoming 'changed to' value)"); + const char *incoming_base_marker = _("||||||| (incoming 'changed from' value)"); const char *separator = "======="; - svn_string_t *original_ascii = - svn_string_create(svn_utf_cstring_from_utf8_fuzzy(original->data, + svn_string_t *incoming_base_ascii = + svn_string_create(svn_utf_cstring_from_utf8_fuzzy(incoming_base->data, scratch_pool), scratch_pool); svn_string_t *mine_ascii = @@ -625,16 +608,18 @@ prop_conflict_from_skel(const svn_string_t **conflict_desc, scratch_pool), scratch_pool); - style = svn_diff_conflict_display_modified_latest; + style = svn_diff_conflict_display_modified_original_latest; stream = svn_stream_from_stringbuf(buf, scratch_pool); SVN_ERR(svn_stream_skip(stream, buf->len)); - SVN_ERR(svn_diff_mem_string_output_merge2(stream, diff, - original_ascii, + SVN_ERR(svn_diff_mem_string_output_merge3(stream, diff, + incoming_base_ascii, mine_ascii, incoming_ascii, - NULL, mine_marker, + incoming_base_marker, mine_marker, incoming_marker, separator, - style, scratch_pool)); + style, + cancel_func, cancel_baton, + scratch_pool)); SVN_ERR(svn_stream_close(stream)); *conflict_desc = svn_string_create_from_buf(buf, result_pool); @@ -669,6 +654,49 @@ prop_conflict_from_skel(const svn_string_t **conflict_desc, return SVN_NO_ERROR; } +/* Parse a property conflict description from the provided SKEL. + The result includes a descriptive message (see generate_conflict_message) + and maybe a diff of property values containing conflict markers. + The result will be allocated in RESULT_POOL. + + Note: SKEL is a single property conflict of the form: + + ("prop" ([ORIGINAL]) ([MINE]) ([INCOMING]) ([INCOMING_BASE])) + + Note: This is not the same format as the property conflicts we store in + wc.db since 1.8. This is the legacy format used in the Workqueue in 1.7-1.8 */ +static svn_error_t * +prop_conflict_from_skel(const svn_string_t **conflict_desc, + const svn_skel_t *skel, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const svn_string_t *original; + const svn_string_t *mine; + const svn_string_t *incoming; + const svn_string_t *incoming_base; + const char *propname; + + /* Navigate to the property name. */ + skel = skel->children->next; + + /* We need to copy these into SCRATCH_POOL in order to nul-terminate + the values. */ + propname = apr_pstrmemdup(scratch_pool, skel->data, skel->len); + original = maybe_prop_value(skel->next, scratch_pool); + mine = maybe_prop_value(skel->next->next, scratch_pool); + incoming = maybe_prop_value(skel->next->next->next, scratch_pool); + incoming_base = maybe_prop_value(skel->next->next->next->next, scratch_pool); + + return svn_error_trace(prop_conflict_new(conflict_desc, + propname, + original, mine, + incoming, incoming_base, + cancel_func, cancel_baton, + result_pool, scratch_pool)); +} /* Create a property conflict file at PREJFILE based on the property conflicts in CONFLICT_SKEL. */ @@ -676,7 +704,9 @@ svn_error_t * svn_wc__create_prejfile(const char **tmp_prejfile_abspath, svn_wc__db_t *db, const char *local_abspath, - const svn_skel_t *conflict_skel, + const svn_skel_t *prop_conflict_data, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -694,11 +724,88 @@ svn_wc__create_prejfile(const char **tmp_prejfile_abspath, tempdir_abspath, svn_io_file_del_none, scratch_pool, iterpool)); - for (scan = conflict_skel->children->next; scan != NULL; scan = scan->next) + if (prop_conflict_data) { - svn_pool_clear(iterpool); + for (scan = prop_conflict_data->children->next; + scan != NULL; scan = scan->next) + { + const svn_string_t *conflict_desc; + + svn_pool_clear(iterpool); + + SVN_ERR(prop_conflict_from_skel(&conflict_desc, scan, + cancel_func, cancel_baton, + iterpool, iterpool)); - SVN_ERR(append_prop_conflict(stream, scan, iterpool)); + SVN_ERR(svn_stream_puts(stream, conflict_desc->data)); + } + } + else + { + svn_wc_operation_t operation; + apr_hash_index_t *hi; + apr_hash_t *old_props; + apr_hash_t *mine_props; + apr_hash_t *their_original_props; + apr_hash_t *their_props; + apr_hash_t *conflicted_props; + svn_skel_t *conflicts; + + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, NULL, + db, local_abspath, + conflicts, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL, + &mine_props, + &their_original_props, + &their_props, + &conflicted_props, + db, local_abspath, + conflicts, + scratch_pool, + scratch_pool)); + + if (operation == svn_wc_operation_merge) + SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath, + scratch_pool, scratch_pool)); + else + old_props = their_original_props; + + /* ### TODO: Sort conflicts? */ + for (hi = apr_hash_first(scratch_pool, conflicted_props); + hi; + hi = apr_hash_next(hi)) + { + const svn_string_t *conflict_desc; + const char *propname = apr_hash_this_key(hi); + const svn_string_t *old_value; + const svn_string_t *mine_value; + const svn_string_t *their_value; + const svn_string_t *their_original_value; + + svn_pool_clear(iterpool); + + old_value = old_props ? svn_hash_gets(old_props, propname) : NULL; + mine_value = mine_props ? svn_hash_gets(mine_props, propname) : NULL; + their_value = their_props ? svn_hash_gets(their_props, propname) + : NULL; + their_original_value = their_original_props + ? svn_hash_gets(their_original_props, propname) + : NULL; + + SVN_ERR(prop_conflict_new(&conflict_desc, + propname, old_value, mine_value, + their_value, their_original_value, + cancel_func, cancel_baton, + iterpool, iterpool)); + + SVN_ERR(svn_stream_puts(stream, conflict_desc->data)); + } } SVN_ERR(svn_stream_close(stream)); @@ -1167,7 +1274,7 @@ svn_wc__merge_props(svn_skel_t **conflict_skel, svn_pool_clear(iterpool); - to_val = to_val ? svn_string_dup(to_val, result_pool) : NULL; + to_val = svn_string_dup(to_val, result_pool); svn_hash_sets(their_props, propname, to_val); @@ -2030,8 +2137,8 @@ svn_wc__canonicalize_props(apr_hash_t **prepared_props, for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)props); hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - const svn_string_t *value = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + const svn_string_t *value = apr_hash_this_val(hi); if (strcmp(name, SVN_PROP_MIME_TYPE) == 0) continue; diff --git a/subversion/libsvn_wc/props.h b/subversion/libsvn_wc/props.h index c648e3c..c33e13e 100644 --- a/subversion/libsvn_wc/props.h +++ b/subversion/libsvn_wc/props.h @@ -139,11 +139,17 @@ svn_wc__get_actual_props(apr_hash_t **props, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Creates a property reject file at *TMP_PREJFILE_ABSPATH, with + either the property conflict data from DB (when PROP_CONFLICT_DATA + is NULL) or the information in PROP_CONFLICT_DATA if it isn't. + */ svn_error_t * svn_wc__create_prejfile(const char **tmp_prejfile_abspath, svn_wc__db_t *db, const char *local_abspath, - const svn_skel_t *conflict_skel, + const svn_skel_t *prop_conflict_data, + svn_cancel_func_t cancel_func, + void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool); diff --git a/subversion/libsvn_wc/questions.c b/subversion/libsvn_wc/questions.c index c2a42b6..0858363 100644 --- a/subversion/libsvn_wc/questions.c +++ b/subversion/libsvn_wc/questions.c @@ -117,6 +117,7 @@ compare_and_verify(svn_boolean_t *modified_p, apr_hash_t *keywords; svn_boolean_t special = FALSE; svn_boolean_t need_translation; + svn_stream_t *v_stream; /* versioned_file */ SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_file_abspath)); @@ -150,22 +151,24 @@ compare_and_verify(svn_boolean_t *modified_p, /* ### Other checks possible? */ - if (need_translation) + /* Reading files is necessary. */ + if (special && need_translation) { - /* Reading files is necessary. */ - svn_stream_t *v_stream; /* versioned_file */ - - if (special) - { - SVN_ERR(svn_subst_read_specialfile(&v_stream, versioned_file_abspath, - scratch_pool, scratch_pool)); - } - else + SVN_ERR(svn_subst_read_specialfile(&v_stream, versioned_file_abspath, + scratch_pool, scratch_pool)); + } + else + { + /* We don't use APR-level buffering because the comparison function + * will do its own buffering. */ + apr_file_t *file; + SVN_ERR(svn_io_file_open(&file, versioned_file_abspath, APR_READ, + APR_OS_DEFAULT, scratch_pool)); + v_stream = svn_stream_from_aprfile2(file, FALSE, scratch_pool); + + if (need_translation) { - SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath, - scratch_pool, scratch_pool)); - - if (!exact_comparison && need_translation) + if (!exact_comparison) { if (eol_style == svn_subst_eol_style_native) eol_str = SVN_SUBST_NATIVE_EOL_STR; @@ -183,7 +186,7 @@ compare_and_verify(svn_boolean_t *modified_p, FALSE /* expand */, scratch_pool); } - else if (need_translation) + else { /* Wrap base stream to translate into working copy form, and * arrange to throw an error if its EOL style is inconsistent. */ @@ -193,21 +196,10 @@ compare_and_verify(svn_boolean_t *modified_p, scratch_pool); } } - - SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream, - scratch_pool)); } - else - { - /* Translation would be a no-op, so compare the original file. */ - svn_stream_t *v_stream; /* versioned_file */ - SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream, - scratch_pool)); - } + SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream, + scratch_pool)); *modified_p = (! same); @@ -377,7 +369,8 @@ internal_conflicted_p(svn_boolean_t *text_conflicted_p, svn_boolean_t resolved_text = FALSE; svn_boolean_t resolved_props = FALSE; - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, + db, local_abspath, scratch_pool, scratch_pool)); if (!conflicts) @@ -604,18 +597,150 @@ svn_wc__has_switched_subtrees(svn_boolean_t *is_switched, } +/* A baton for use with modcheck_found_entry(). */ +typedef struct modcheck_baton_t { + svn_boolean_t ignore_unversioned; + svn_boolean_t found_mod; /* whether a modification has been found */ + svn_boolean_t found_not_delete; /* Found a not-delete modification */ +} modcheck_baton_t; + +/* An implementation of svn_wc_status_func4_t. */ +static svn_error_t * +modcheck_callback(void *baton, + const char *local_abspath, + const svn_wc_status3_t *status, + apr_pool_t *scratch_pool) +{ + modcheck_baton_t *mb = baton; + + switch (status->node_status) + { + case svn_wc_status_normal: + case svn_wc_status_ignored: + case svn_wc_status_none: + case svn_wc_status_external: + break; + + case svn_wc_status_incomplete: + if ((status->text_status != svn_wc_status_normal + && status->text_status != svn_wc_status_none) + || (status->prop_status != svn_wc_status_normal + && status->prop_status != svn_wc_status_none)) + { + mb->found_mod = TRUE; + mb->found_not_delete = TRUE; + /* Incomplete, but local modifications */ + return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); + } + break; + + case svn_wc_status_deleted: + mb->found_mod = TRUE; + if (!mb->ignore_unversioned + && status->actual_kind != svn_node_none + && status->actual_kind != svn_node_unknown) + { + /* The delete is obstructed by something unversioned */ + mb->found_not_delete = TRUE; + return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); + } + break; + + case svn_wc_status_unversioned: + if (mb->ignore_unversioned) + break; + /* else fall through */ + case svn_wc_status_missing: + case svn_wc_status_obstructed: + mb->found_mod = TRUE; + mb->found_not_delete = TRUE; + /* Exit from the status walker: We know what we want to know */ + return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); + + default: + case svn_wc_status_added: + case svn_wc_status_replaced: + case svn_wc_status_modified: + mb->found_mod = TRUE; + mb->found_not_delete = TRUE; + /* Exit from the status walker: We know what we want to know */ + return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); + } + + return SVN_NO_ERROR; +} + + +/* Set *MODIFIED to true iff there are any local modifications within the + * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED + * is set to true and all the local modifications were deletes then set + * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH + * may be a file or a directory. */ +svn_error_t * +svn_wc__node_has_local_mods(svn_boolean_t *modified, + svn_boolean_t *all_edits_are_deletes, + svn_wc__db_t *db, + const char *local_abspath, + svn_boolean_t ignore_unversioned, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + modcheck_baton_t modcheck_baton = { FALSE, FALSE, FALSE }; + svn_error_t *err; + + if (!all_edits_are_deletes) + { + SVN_ERR(svn_wc__db_has_db_mods(modified, db, local_abspath, + scratch_pool)); + + if (*modified) + return SVN_NO_ERROR; + } + + modcheck_baton.ignore_unversioned = ignore_unversioned; + + /* Walk the WC tree for status with depth infinity, looking for any local + * modifications. If it's a "sparse" directory, that's OK: there can be + * no local mods in the pieces that aren't present in the WC. */ + + err = svn_wc__internal_walk_status(db, local_abspath, + svn_depth_infinity, + FALSE, FALSE, FALSE, NULL, + modcheck_callback, &modcheck_baton, + cancel_func, cancel_baton, + scratch_pool); + + if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) + svn_error_clear(err); + else + SVN_ERR(err); + + *modified = modcheck_baton.found_mod; + if (all_edits_are_deletes) + *all_edits_are_deletes = (modcheck_baton.found_mod + && !modcheck_baton.found_not_delete); + + return SVN_NO_ERROR; +} + svn_error_t * svn_wc__has_local_mods(svn_boolean_t *is_modified, svn_wc_context_t *wc_ctx, const char *local_abspath, + svn_boolean_t ignore_unversioned, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { - return svn_error_trace(svn_wc__db_has_local_mods(is_modified, - wc_ctx->db, - local_abspath, - cancel_func, - cancel_baton, - scratch_pool)); + svn_boolean_t modified; + + SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL, + wc_ctx->db, local_abspath, + ignore_unversioned, + cancel_func, cancel_baton, + scratch_pool)); + + *is_modified = modified; + return SVN_NO_ERROR; } diff --git a/subversion/libsvn_wc/relocate.c b/subversion/libsvn_wc/relocate.c index 4a9df67..e4b335b 100644 --- a/subversion/libsvn_wc/relocate.c +++ b/subversion/libsvn_wc/relocate.c @@ -148,7 +148,7 @@ svn_wc_relocate4(svn_wc_context_t *wc_ctx, if (old_url_len == from_len) new_url = to; else - new_url = apr_pstrcat(scratch_pool, to, old_url + from_len, (char *)NULL); + new_url = apr_pstrcat(scratch_pool, to, old_url + from_len, SVN_VA_NULL); if (! svn_path_is_url(new_url)) return svn_error_createf(SVN_ERR_WC_INVALID_RELOCATION, NULL, _("Invalid relocation destination: '%s' " diff --git a/subversion/libsvn_wc/revert.c b/subversion/libsvn_wc/revert.c index 5e190e8..bba1799 100644 --- a/subversion/libsvn_wc/revert.c +++ b/subversion/libsvn_wc/revert.c @@ -46,6 +46,7 @@ #include "svn_private_config.h" #include "private/svn_io_private.h" #include "private/svn_wc_private.h" +#include "private/svn_sorts_private.h" /* Thoughts on Reversion. @@ -154,7 +155,7 @@ revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, void *cancel_baton, apr_pool_t *scratch_pool) { - const apr_array_header_t *copied_children; + apr_array_header_t *copied_children; svn_wc__db_revert_list_copied_child_info_t *child_info; int i; svn_node_kind_t on_disk; @@ -198,9 +199,7 @@ revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, * that still exist on disk (e.g. unversioned files within the copied tree). * So sort the children list such that longest paths come first and try to * remove each child directory in order. */ - qsort(copied_children->elts, copied_children->nelts, - sizeof(svn_wc__db_revert_list_copied_child_info_t *), - compare_revert_list_copied_children); + svn_sort__array(copied_children, compare_revert_list_copied_children); for (i = 0; i < copied_children->nelts; i++) { child_info = APR_ARRAY_IDX( @@ -247,6 +246,22 @@ revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, return SVN_NO_ERROR; } +/* Forward definition */ +static svn_error_t * +revert_wc_data(svn_boolean_t *run_wq, + svn_boolean_t *notify_required, + svn_wc__db_t *db, + const char *local_abspath, + svn_wc__db_status_t status, + svn_node_kind_t kind, + svn_node_kind_t reverted_kind, + svn_filesize_t recorded_size, + apr_time_t recorded_time, + svn_boolean_t copied_here, + svn_boolean_t use_commit_times, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool); /* Make the working tree under LOCAL_ABSPATH to depth DEPTH match the versioned tree. This function is called after svn_wc__db_op_revert @@ -255,56 +270,63 @@ revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, REVERT_ROOT is true for explicit revert targets and FALSE for targets reached via recursion. + + Sets *RUN_WQ to TRUE when the caller should (eventually) run the workqueue. + (The function sets it to FALSE when it has run the WQ itself) + + If INFO is NULL, LOCAL_ABSPATH doesn't exist in DB. Otherwise INFO + specifies the state of LOCAL_ABSPATH in DB. */ static svn_error_t * -revert_restore(svn_wc__db_t *db, +revert_restore(svn_boolean_t *run_wq, + svn_wc__db_t *db, const char *local_abspath, svn_depth_t depth, + svn_boolean_t metadata_only, svn_boolean_t use_commit_times, svn_boolean_t revert_root, + const struct svn_wc__db_info_t *info, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) { - svn_error_t *err; svn_wc__db_status_t status; svn_node_kind_t kind; - svn_node_kind_t on_disk; svn_boolean_t notify_required; const apr_array_header_t *conflict_files; svn_filesize_t recorded_size; apr_time_t recorded_time; - apr_finfo_t finfo; -#ifdef HAVE_SYMLINK - svn_boolean_t special; -#endif svn_boolean_t copied_here; svn_node_kind_t reverted_kind; - svn_boolean_t is_wcroot; - if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); - SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, scratch_pool)); - if (is_wcroot && !revert_root) + if (!revert_root) { - /* Issue #4162: Obstructing working copy. We can't access the working - copy data from the parent working copy for this node by just using - local_abspath */ + svn_boolean_t is_wcroot; - if (notify_func) + SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, scratch_pool)); + if (is_wcroot) { - svn_wc_notify_t *notify = svn_wc_create_notify( + /* Issue #4162: Obstructing working copy. We can't access the working + copy data from the parent working copy for this node by just using + local_abspath */ + + if (notify_func) + { + svn_wc_notify_t *notify = + svn_wc_create_notify( local_abspath, svn_wc_notify_update_skip_obstruction, scratch_pool); - notify_func(notify_baton, notify, scratch_pool); - } + notify_func(notify_baton, notify, scratch_pool); + } - return SVN_NO_ERROR; /* We don't revert obstructing working copies */ + return SVN_NO_ERROR; /* We don't revert obstructing working copies */ + } } SVN_ERR(svn_wc__db_revert_list_read(¬ify_required, @@ -313,17 +335,15 @@ revert_restore(svn_wc__db_t *db, db, local_abspath, scratch_pool, scratch_pool)); - err = svn_wc__db_read_info(&status, &kind, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &recorded_size, &recorded_time, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - db, local_abspath, scratch_pool, scratch_pool); - - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + if (info) + { + status = info->status; + kind = info->kind; + recorded_size = info->recorded_size; + recorded_time = info->recorded_time; + } + else { - svn_error_clear(err); - if (!copied_here) { if (notify_func && notify_required) @@ -350,9 +370,116 @@ revert_restore(svn_wc__db_t *db, recorded_time = 0; } } - else if (err) - return svn_error_trace(err); + if (!metadata_only) + { + SVN_ERR(revert_wc_data(run_wq, + ¬ify_required, + db, local_abspath, status, kind, + reverted_kind, recorded_size, recorded_time, + copied_here, use_commit_times, + cancel_func, cancel_baton, scratch_pool)); + } + + /* We delete these marker files even though they are not strictly metadata. + But for users that use revert as an API with metadata_only, these are. */ + if (conflict_files) + { + int i; + for (i = 0; i < conflict_files->nelts; i++) + { + SVN_ERR(remove_conflict_file(¬ify_required, + APR_ARRAY_IDX(conflict_files, i, + const char *), + local_abspath, scratch_pool)); + } + } + + if (notify_func && notify_required) + notify_func(notify_baton, + svn_wc_create_notify(local_abspath, svn_wc_notify_revert, + scratch_pool), + scratch_pool); + + if (depth == svn_depth_infinity && kind == svn_node_dir) + { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_t *children, *conflicts; + apr_hash_index_t *hi; + + SVN_ERR(revert_restore_handle_copied_dirs(NULL, db, local_abspath, FALSE, + cancel_func, cancel_baton, + iterpool)); + + SVN_ERR(svn_wc__db_read_children_info(&children, &conflicts, + db, local_abspath, FALSE, + scratch_pool, iterpool)); + + for (hi = apr_hash_first(scratch_pool, children); + hi; + hi = apr_hash_next(hi)) + { + const char *child_name = apr_hash_this_key(hi); + const char *child_abspath; + + svn_pool_clear(iterpool); + + child_abspath = svn_dirent_join(local_abspath, child_name, iterpool); + + SVN_ERR(revert_restore(run_wq, + db, child_abspath, depth, metadata_only, + use_commit_times, FALSE /* revert root */, + apr_hash_this_val(hi), + cancel_func, cancel_baton, + notify_func, notify_baton, + iterpool)); + } + + /* Run the queue per directory */ + if (*run_wq) + { + SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, + iterpool)); + *run_wq = FALSE; + } + + svn_pool_destroy(iterpool); + } + + if (notify_func && (revert_root || kind == svn_node_dir)) + SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton, + db, local_abspath, scratch_pool)); + + return SVN_NO_ERROR; +} + +/* Perform the in-working copy revert of LOCAL_ABSPATH, to what is stored in DB */ +static svn_error_t * +revert_wc_data(svn_boolean_t *run_wq, + svn_boolean_t *notify_required, + svn_wc__db_t *db, + const char *local_abspath, + svn_wc__db_status_t status, + svn_node_kind_t kind, + svn_node_kind_t reverted_kind, + svn_filesize_t recorded_size, + apr_time_t recorded_time, + svn_boolean_t copied_here, + svn_boolean_t use_commit_times, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + apr_finfo_t finfo; + svn_node_kind_t on_disk; +#ifdef HAVE_SYMLINK + svn_boolean_t special; +#endif + + /* Would be nice to use svn_io_dirent2_t here, but the performance + improvement that provides doesn't work, because we need the read + only and executable bits later on, in the most likely code path */ err = svn_io_stat(&finfo, local_abspath, APR_FINFO_TYPE | APR_FINFO_LINK | APR_FINFO_SIZE | APR_FINFO_MTIME @@ -478,14 +605,14 @@ revert_restore(svn_wc__db_t *db, modified = FALSE; } else + /* Side effect: fixes recorded timestamps */ SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, local_abspath, TRUE, scratch_pool)); if (modified) { - SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, - scratch_pool)); + /* Install will replace the file */ on_disk = svn_node_none; } else @@ -505,14 +632,14 @@ revert_restore(svn_wc__db_t *db, SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool)); - notify_required = TRUE; + *notify_required = TRUE; } else if (!needs_lock_prop && read_only) { SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool)); - notify_required = TRUE; + *notify_required = TRUE; } } @@ -533,14 +660,14 @@ revert_restore(svn_wc__db_t *db, SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE, scratch_pool)); - notify_required = TRUE; + *notify_required = TRUE; } else if (!executable_prop && executable) { SVN_ERR(svn_io_set_file_executable(local_abspath, FALSE, FALSE, scratch_pool)); - notify_required = TRUE; + *notify_required = TRUE; } } #endif @@ -564,90 +691,36 @@ revert_restore(svn_wc__db_t *db, { svn_skel_t *work_item; - /* ### Get the checksum from read_info above and pass in here? */ SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, local_abspath, NULL, use_commit_times, TRUE, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_item, scratch_pool)); - SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, - scratch_pool)); + *run_wq = TRUE; } - notify_required = TRUE; + *notify_required = TRUE; } - if (conflict_files) - { - int i; - for (i = 0; i < conflict_files->nelts; i++) - { - SVN_ERR(remove_conflict_file(¬ify_required, - APR_ARRAY_IDX(conflict_files, i, - const char *), - local_abspath, scratch_pool)); - } - } - - if (notify_func && notify_required) - notify_func(notify_baton, - svn_wc_create_notify(local_abspath, svn_wc_notify_revert, - scratch_pool), - scratch_pool); - - if (depth == svn_depth_infinity && kind == svn_node_dir) - { - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - const apr_array_header_t *children; - int i; - - SVN_ERR(revert_restore_handle_copied_dirs(NULL, db, local_abspath, FALSE, - cancel_func, cancel_baton, - iterpool)); - - SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db, - local_abspath, - scratch_pool, - iterpool)); - for (i = 0; i < children->nelts; ++i) - { - const char *child_abspath; - - svn_pool_clear(iterpool); - - child_abspath = svn_dirent_join(local_abspath, - APR_ARRAY_IDX(children, i, - const char *), - iterpool); - - SVN_ERR(revert_restore(db, child_abspath, depth, - use_commit_times, FALSE /* revert root */, - cancel_func, cancel_baton, - notify_func, notify_baton, - iterpool)); - } - - svn_pool_destroy(iterpool); - } - - if (notify_func) - SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton, - db, local_abspath, scratch_pool)); return SVN_NO_ERROR; } - -svn_error_t * -svn_wc__revert_internal(svn_wc__db_t *db, - const char *local_abspath, - svn_depth_t depth, - svn_boolean_t use_commit_times, - svn_cancel_func_t cancel_func, - void *cancel_baton, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) +/* Revert tree LOCAL_ABSPATH to depth DEPTH and notify for all reverts. */ +static svn_error_t * +revert(svn_wc__db_t *db, + const char *local_abspath, + svn_depth_t depth, + svn_boolean_t use_commit_times, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, + svn_cancel_func_t cancel_func, + void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool) { svn_error_t *err; + const struct svn_wc__db_info_t *info = NULL; + svn_boolean_t run_queue = FALSE; SVN_ERR_ASSERT(depth == svn_depth_empty || depth == svn_depth_infinity); @@ -667,15 +740,37 @@ svn_wc__revert_internal(svn_wc__db_t *db, SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool)); } - err = svn_wc__db_op_revert(db, local_abspath, depth, - scratch_pool, scratch_pool); + err = svn_error_trace( + svn_wc__db_op_revert(db, local_abspath, depth, clear_changelists, + scratch_pool, scratch_pool)); if (!err) - err = revert_restore(db, local_abspath, depth, - use_commit_times, TRUE /* revert root */, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool); + { + err = svn_error_trace( + svn_wc__db_read_single_info(&info, db, local_abspath, FALSE, + scratch_pool, scratch_pool)); + + if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + { + svn_error_clear(err); + err = NULL; + info = NULL; + } + } + + if (!err) + err = svn_error_trace( + revert_restore(&run_queue, db, local_abspath, depth, metadata_only, + use_commit_times, TRUE /* revert root */, + info, cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool)); + + if (run_queue) + err = svn_error_compose_create(err, + svn_wc__wq_run(db, local_abspath, + cancel_func, cancel_baton, + scratch_pool)); err = svn_error_compose_create(err, svn_wc__db_revert_list_done(db, @@ -694,6 +789,8 @@ revert_changelist(svn_wc__db_t *db, svn_depth_t depth, svn_boolean_t use_commit_times, apr_hash_t *changelist_hash, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -710,11 +807,12 @@ revert_changelist(svn_wc__db_t *db, /* Revert this node (depth=empty) if it matches one of the changelists. */ if (svn_wc__internal_changelist_match(db, local_abspath, changelist_hash, scratch_pool)) - SVN_ERR(svn_wc__revert_internal(db, local_abspath, - svn_depth_empty, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool)); + SVN_ERR(revert(db, local_abspath, + svn_depth_empty, use_commit_times, clear_changelists, + metadata_only, + cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool)); if (depth == svn_depth_empty) return SVN_NO_ERROR; @@ -746,6 +844,7 @@ revert_changelist(svn_wc__db_t *db, SVN_ERR(revert_changelist(db, child_abspath, depth, use_commit_times, changelist_hash, + clear_changelists, metadata_only, cancel_func, cancel_baton, notify_func, notify_baton, iterpool)); @@ -770,6 +869,8 @@ revert_partial(svn_wc__db_t *db, const char *local_abspath, svn_depth_t depth, svn_boolean_t use_commit_times, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -789,9 +890,10 @@ revert_partial(svn_wc__db_t *db, /* Revert the root node itself (depth=empty), then move on to the children. */ - SVN_ERR(svn_wc__revert_internal(db, local_abspath, svn_depth_empty, - use_commit_times, cancel_func, cancel_baton, - notify_func, notify_baton, iterpool)); + SVN_ERR(revert(db, local_abspath, svn_depth_empty, + use_commit_times, clear_changelists, metadata_only, + cancel_func, cancel_baton, + notify_func, notify_baton, iterpool)); SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db, local_abspath, @@ -822,11 +924,12 @@ revert_partial(svn_wc__db_t *db, } /* Revert just this node (depth=empty). */ - SVN_ERR(svn_wc__revert_internal(db, child_abspath, - svn_depth_empty, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - iterpool)); + SVN_ERR(revert(db, child_abspath, + svn_depth_empty, use_commit_times, clear_changelists, + metadata_only, + cancel_func, cancel_baton, + notify_func, notify_baton, + iterpool)); } svn_pool_destroy(iterpool); @@ -836,11 +939,13 @@ revert_partial(svn_wc__db_t *db, svn_error_t * -svn_wc_revert4(svn_wc_context_t *wc_ctx, +svn_wc_revert5(svn_wc_context_t *wc_ctx, const char *local_abspath, svn_depth_t depth, svn_boolean_t use_commit_times, const apr_array_header_t *changelist_filter, + svn_boolean_t clear_changelists, + svn_boolean_t metadata_only, svn_cancel_func_t cancel_func, void *cancel_baton, svn_wc_notify_func2_t notify_func, @@ -856,17 +961,20 @@ svn_wc_revert4(svn_wc_context_t *wc_ctx, return svn_error_trace(revert_changelist(wc_ctx->db, local_abspath, depth, use_commit_times, changelist_hash, + clear_changelists, + metadata_only, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool)); } if (depth == svn_depth_empty || depth == svn_depth_infinity) - return svn_error_trace(svn_wc__revert_internal(wc_ctx->db, local_abspath, - depth, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool)); + return svn_error_trace(revert(wc_ctx->db, local_abspath, + depth, use_commit_times, clear_changelists, + metadata_only, + cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool)); /* The user may expect svn_depth_files/svn_depth_immediates to work on copied dirs with one level of children. It doesn't, the user @@ -877,6 +985,7 @@ svn_wc_revert4(svn_wc_context_t *wc_ctx, if (depth == svn_depth_files || depth == svn_depth_immediates) return svn_error_trace(revert_partial(wc_ctx->db, local_abspath, depth, use_commit_times, + clear_changelists, metadata_only, cancel_func, cancel_baton, notify_func, notify_baton, scratch_pool)); diff --git a/subversion/libsvn_wc/revision_status.c b/subversion/libsvn_wc/revision_status.c index a4b9bea..c53c45e 100644 --- a/subversion/libsvn_wc/revision_status.c +++ b/subversion/libsvn_wc/revision_status.c @@ -60,8 +60,14 @@ svn_wc_revision_status2(svn_wc_revision_status_t **result_p, &result->modified, &result->switched, wc_ctx->db, local_abspath, trail_url, - committed, cancel_func, cancel_baton, + committed, scratch_pool)); + if (!result->modified) + SVN_ERR(svn_wc__node_has_local_mods(&result->modified, NULL, + wc_ctx->db, local_abspath, TRUE, + cancel_func, cancel_baton, + scratch_pool)); + return SVN_NO_ERROR; } diff --git a/subversion/libsvn_wc/status.c b/subversion/libsvn_wc/status.c index fa57b0a..83fd3d4 100644 --- a/subversion/libsvn_wc/status.c +++ b/subversion/libsvn_wc/status.c @@ -36,7 +36,6 @@ #include "svn_string.h" #include "svn_error.h" #include "svn_dirent_uri.h" -#include "svn_path.h" #include "svn_io.h" #include "svn_config.h" #include "svn_time.h" @@ -47,15 +46,33 @@ #include "wc.h" #include "props.h" -#include "entries.h" -#include "translate.h" -#include "tree_conflicts.h" +#include "private/svn_sorts_private.h" #include "private/svn_wc_private.h" #include "private/svn_fspath.h" #include "private/svn_editor.h" +/* The file internal variant of svn_wc_status3_t, with slightly more + data. + + Instead of directly creating svn_wc_status3_t instances, we really + create instances of this struct with slightly more data for processing + by the status walker and status editor. + + svn_wc_status3_dup() allocates space for this struct, but doesn't + copy the actual data. The remaining fields are copied by hash_stash(), + which is where the status editor stashes information for producing + later. */ +typedef struct svn_wc__internal_status_t +{ + svn_wc_status3_t s; /* First member; same pointer*/ + + svn_boolean_t has_descendants; + + /* Make sure to update hash_stash() when adding values here */ +} svn_wc__internal_status_t; + /*** Baton used for walking the local status */ struct walk_status_baton @@ -70,6 +87,9 @@ struct walk_status_baton /* Should we ignore text modifications? */ svn_boolean_t ignore_text_mods; + /* Scan the working copy for local modifications and missing nodes. */ + svn_boolean_t check_working_copy; + /* Externals info harvested during the status run. */ apr_hash_t *externals; @@ -92,7 +112,6 @@ struct edit_baton /* The DB handle for managing the working copy state. */ svn_wc__db_t *db; - svn_wc_context_t *wc_ctx; /* The overall depth of this edit (a dir baton may override this). * @@ -127,7 +146,7 @@ struct edit_baton const apr_array_header_t *ignores; /* Status item for the path represented by the anchor of the edit. */ - svn_wc_status3_t *anchor_status; + svn_wc__internal_status_t *anchor_status; /* Was open_root() called for this edit drive? */ svn_boolean_t root_opened; @@ -276,63 +295,23 @@ get_repos_root_url_relpath(const char **repos_relpath, *repos_root_url = apr_pstrdup(result_pool, parent_repos_root_url); *repos_uuid = apr_pstrdup(result_pool, parent_repos_uuid); } - else if (info->status == svn_wc__db_status_added) - { - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, - repos_relpath, repos_root_url, - repos_uuid, NULL, NULL, NULL, NULL, - db, local_abspath, - result_pool, scratch_pool)); - } - else if (info->status == svn_wc__db_status_deleted - && !info->have_more_work - && info->have_base) + else { - SVN_ERR(svn_wc__db_scan_base_repos(repos_relpath, repos_root_url, + SVN_ERR(svn_wc__db_read_repos_info(NULL, + repos_relpath, repos_root_url, repos_uuid, db, local_abspath, result_pool, scratch_pool)); } - else if (info->status == svn_wc__db_status_deleted) - { - const char *work_del_abspath; - const char *add_abspath; - - /* Handles working DELETE and the special case where there is just - svn_wc__db_status_not_present in WORKING */ - - SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, &work_del_abspath, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - - /* The parent of what has been deleted must be added */ - add_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool); - - SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, repos_relpath, - repos_root_url, repos_uuid, NULL, - NULL, NULL, NULL, - db, add_abspath, - result_pool, scratch_pool)); - *repos_relpath = svn_relpath_join(*repos_relpath, - svn_dirent_skip_ancestor( - add_abspath, - local_abspath), - result_pool); - } - else - { - *repos_relpath = NULL; - *repos_root_url = NULL; - *repos_uuid = NULL; - } return SVN_NO_ERROR; } static svn_error_t * -internal_status(svn_wc_status3_t **status, +internal_status(svn_wc__internal_status_t **status, svn_wc__db_t *db, const char *local_abspath, + svn_boolean_t check_working_copy, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -350,12 +329,13 @@ internal_status(svn_wc_status3_t **status, *STATUS will be set to NULL. If GET_ALL is non-zero, then *STATUS will be allocated and returned no matter what. If IGNORE_TEXT_MODS is TRUE then don't check for text mods, assume there are none and set and *STATUS - returned to reflect that assumption. + returned to reflect that assumption. If CHECK_WORKING_COPY is FALSE, + do not adjust the result for missing working copy files. The status struct's repos_lock field will be set to REPOS_LOCK. */ static svn_error_t * -assemble_status(svn_wc_status3_t **status, +assemble_status(svn_wc__internal_status_t **status, svn_wc__db_t *db, const char *local_abspath, const char *parent_repos_root_url, @@ -365,18 +345,17 @@ assemble_status(svn_wc_status3_t **status, const svn_io_dirent2_t *dirent, svn_boolean_t get_all, svn_boolean_t ignore_text_mods, + svn_boolean_t check_working_copy, const svn_lock_t *repos_lock, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_wc__internal_status_t *inner_stat; svn_wc_status3_t *stat; svn_boolean_t switched_p = FALSE; svn_boolean_t copied = FALSE; svn_boolean_t conflicted; const char *moved_from_abspath = NULL; - svn_filesize_t filesize = (dirent && (dirent->kind == svn_node_file)) - ? dirent->filesize - : SVN_INVALID_FILESIZE; /* Defaults for two main variables. */ enum svn_wc_status_kind node_status = svn_wc_status_normal; @@ -384,10 +363,6 @@ assemble_status(svn_wc_status3_t **status, enum svn_wc_status_kind prop_status = svn_wc_status_none; - if (!info) - SVN_ERR(svn_wc__db_read_single_info(&info, db, local_abspath, - result_pool, scratch_pool)); - if (!info->repos_relpath || !parent_repos_relpath) switched_p = FALSE; else @@ -426,7 +401,7 @@ assemble_status(svn_wc_status3_t **status, copied = TRUE; /* Working deletion */ } } - else + else if (check_working_copy) { /* Examine whether our target is missing or obstructed. To detect * obstructions, we have to look at the on-disk status in DIRENT. */ @@ -606,19 +581,21 @@ assemble_status(svn_wc_status3_t **status, && prop_status != svn_wc_status_none) node_status = prop_status; - /* 5. Easy out: unless we're fetching -every- entry, don't bother - to allocate a struct for an uninteresting entry. */ + /* 5. Easy out: unless we're fetching -every- node, don't bother + to allocate a struct for an uninteresting node. + This filter should match the filter in is_sendable_status() */ if (! get_all) if (((node_status == svn_wc_status_none) || (node_status == svn_wc_status_normal)) && (! switched_p) - && (! info->locked ) + && (! info->locked) && (! info->lock) && (! repos_lock) && (! info->changelist) - && (! conflicted)) + && (! conflicted) + && (! info->moved_to)) { *status = NULL; return SVN_NO_ERROR; @@ -626,7 +603,9 @@ assemble_status(svn_wc_status3_t **status, /* 6. Build and return a status structure. */ - stat = apr_pcalloc(result_pool, sizeof(**status)); + inner_stat = apr_pcalloc(result_pool, sizeof(*inner_stat)); + stat = &inner_stat->s; + inner_stat->has_descendants = info->has_descendants; switch (info->kind) { @@ -642,7 +621,22 @@ assemble_status(svn_wc_status3_t **status, stat->kind = svn_node_unknown; } stat->depth = info->depth; - stat->filesize = filesize; + + if (dirent) + { + stat->filesize = (dirent->kind == svn_node_file) + ? dirent->filesize + : SVN_INVALID_FILESIZE; + stat->actual_kind = dirent->special ? svn_node_symlink + : dirent->kind; + } + else + { + stat->filesize = SVN_INVALID_FILESIZE; + stat->actual_kind = ignore_text_mods ? svn_node_unknown + : svn_node_none; + } + stat->node_status = node_status; stat->text_status = text_status; stat->prop_status = prop_status; @@ -700,7 +694,7 @@ assemble_status(svn_wc_status3_t **status, stat->file_external = info->file_external; - *status = stat; + *status = inner_stat; return SVN_NO_ERROR; } @@ -714,7 +708,7 @@ assemble_status(svn_wc_status3_t **status, node_status to svn_wc_status_unversioned. */ static svn_error_t * -assemble_unversioned(svn_wc_status3_t **status, +assemble_unversioned(svn_wc__internal_status_t **status, svn_wc__db_t *db, const char *local_abspath, const svn_io_dirent2_t *dirent, @@ -723,17 +717,30 @@ assemble_unversioned(svn_wc_status3_t **status, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_wc__internal_status_t *inner_status; svn_wc_status3_t *stat; /* return a fairly blank structure. */ - stat = apr_pcalloc(result_pool, sizeof(*stat)); + inner_status = apr_pcalloc(result_pool, sizeof(*inner_status)); + stat = &inner_status->s; /*stat->versioned = FALSE;*/ stat->kind = svn_node_unknown; /* not versioned */ stat->depth = svn_depth_unknown; - stat->filesize = (dirent && dirent->kind == svn_node_file) - ? dirent->filesize - : SVN_INVALID_FILESIZE; + if (dirent) + { + stat->actual_kind = dirent->special ? svn_node_symlink + : dirent->kind; + stat->filesize = (dirent->kind == svn_node_file) + ? dirent->filesize + : SVN_INVALID_FILESIZE; + } + else + { + stat->actual_kind = svn_node_none; + stat->filesize = SVN_INVALID_FILESIZE; + } + stat->node_status = svn_wc_status_none; stat->text_status = svn_wc_status_none; stat->prop_status = svn_wc_status_none; @@ -770,7 +777,7 @@ assemble_unversioned(svn_wc_status3_t **status, stat->conflicted = tree_conflicted; stat->changelist = NULL; - *status = stat; + *status = inner_status; return SVN_NO_ERROR; } @@ -791,7 +798,7 @@ send_status_structure(const struct walk_status_baton *wb, void *status_baton, apr_pool_t *scratch_pool) { - svn_wc_status3_t *statstruct; + svn_wc__internal_status_t *statstruct; const svn_lock_t *repos_lock = NULL; /* Check for a repository lock. */ @@ -819,12 +826,14 @@ send_status_structure(const struct walk_status_baton *wb, SVN_ERR(assemble_status(&statstruct, wb->db, local_abspath, parent_repos_root_url, parent_repos_relpath, parent_repos_uuid, - info, dirent, get_all, wb->ignore_text_mods, + info, dirent, get_all, + wb->ignore_text_mods, wb->check_working_copy, repos_lock, scratch_pool, scratch_pool)); if (statstruct && status_func) return svn_error_trace((*status_func)(status_baton, local_abspath, - statstruct, scratch_pool)); + &statstruct->s, + scratch_pool)); return SVN_NO_ERROR; } @@ -940,7 +949,7 @@ is_external_path(apr_hash_t *externals, hi; hi = apr_hash_next(hi)) { - const char *external_abspath = svn__apr_hash_index_key(hi); + const char *external_abspath = apr_hash_this_key(hi); if (svn_dirent_is_child(local_abspath, external_abspath, NULL)) return TRUE; @@ -980,7 +989,7 @@ send_unversioned_item(const struct walk_status_baton *wb, { svn_boolean_t is_ignored; svn_boolean_t is_external; - svn_wc_status3_t *status; + svn_wc__internal_status_t *status; const char *base_name = svn_dirent_basename(local_abspath, NULL); is_ignored = svn_wc_match_ignore_list(base_name, patterns, scratch_pool); @@ -992,12 +1001,12 @@ send_unversioned_item(const struct walk_status_baton *wb, is_external = is_external_path(wb->externals, local_abspath, scratch_pool); if (is_external) - status->node_status = svn_wc_status_external; + status->s.node_status = svn_wc_status_external; /* We can have a tree conflict on an unversioned path, i.e. an incoming * delete on a locally deleted path during an update. Don't ever ignore * those! */ - if (status->conflicted) + if (status->s.conflicted) is_ignored = FALSE; /* If we aren't ignoring it, or if it's an externals path, pass this @@ -1006,7 +1015,7 @@ send_unversioned_item(const struct walk_status_baton *wb, || !is_ignored || is_external) return svn_error_trace((*status_func)(status_baton, local_abspath, - status, scratch_pool)); + &status->s, scratch_pool)); return SVN_NO_ERROR; } @@ -1109,7 +1118,7 @@ one_child_status(const struct walk_status_baton *wb, /* Descend in subdirectories. */ if (depth == svn_depth_infinity - && info->kind == svn_node_dir) + && info->has_descendants /* is dir, or was dir and tc descendants */) { SVN_ERR(get_dir_status(wb, local_abspath, TRUE, dir_repos_root_url, dir_repos_relpath, @@ -1132,11 +1141,16 @@ one_child_status(const struct walk_status_baton *wb, * look up the kinds in the conflict ... just show all. */ if (! conflicted) { - /* Selected node, but not found */ - if (dirent == NULL) - return SVN_NO_ERROR; + /* We have a node, but its not visible in the WC. It can be a marker + node (not present, (server) excluded), *or* it can be the explictly + passed target of the status walk operation that doesn't exist. - if (depth == svn_depth_files && dirent->kind == svn_node_dir) + We only report the node when the caller explicitly as + */ + if (dirent == NULL && strcmp(wb->target_abspath, local_abspath) != 0) + return SVN_NO_ERROR; /* Marker node */ + + if (depth == svn_depth_files && dirent && dirent->kind == svn_node_dir) return SVN_NO_ERROR; if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL), @@ -1228,21 +1242,28 @@ get_dir_status(const struct walk_status_baton *wb, iterpool = svn_pool_create(scratch_pool); - err = svn_io_get_dirents3(&dirents, local_abspath, FALSE, scratch_pool, - iterpool); - if (err - && (APR_STATUS_IS_ENOENT(err->apr_err) - || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) + if (wb->check_working_copy) { - svn_error_clear(err); - dirents = apr_hash_make(scratch_pool); + err = svn_io_get_dirents3(&dirents, local_abspath, + wb->ignore_text_mods /* only_check_type*/, + scratch_pool, iterpool); + if (err + && (APR_STATUS_IS_ENOENT(err->apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) + { + svn_error_clear(err); + dirents = apr_hash_make(scratch_pool); + } + else + SVN_ERR(err); } else - SVN_ERR(err); + dirents = apr_hash_make(scratch_pool); if (!dir_info) - SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, local_abspath, - scratch_pool, iterpool)); + SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, local_abspath, + !wb->check_working_copy, + scratch_pool, iterpool)); SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url, &dir_repos_uuid, dir_info, @@ -1256,6 +1277,7 @@ get_dir_status(const struct walk_status_baton *wb, hash are subsequently used. */ SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, wb->db, local_abspath, + !wb->check_working_copy, scratch_pool, iterpool)); all_children = apr_hash_overlay(scratch_pool, nodes, dirents); @@ -1404,6 +1426,7 @@ get_child_status(const struct walk_status_baton *wb, SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, parent_abspath, + !wb->check_working_copy, scratch_pool, scratch_pool)); SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url, @@ -1455,9 +1478,15 @@ hash_stash(void *baton, { apr_hash_t *stat_hash = baton; apr_pool_t *hash_pool = apr_hash_pool_get(stat_hash); + void *new_status = svn_wc_dup_status3(status, hash_pool); + const svn_wc__internal_status_t *old_status = (const void*)status; + + /* Copy the internal/private data. */ + svn_wc__internal_status_t *is = new_status; + is->has_descendants = old_status->has_descendants; + assert(! svn_hash_gets(stat_hash, path)); - svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path), - svn_wc_dup_status3(status, hash_pool)); + svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path), new_status); return SVN_NO_ERROR; } @@ -1497,6 +1526,7 @@ tweak_statushash(void *baton, void *this_dir_baton, svn_boolean_t is_dir_baton, svn_wc__db_t *db, + svn_boolean_t check_working_copy, const char *local_abspath, enum svn_wc_status_kind repos_node_status, enum svn_wc_status_kind repos_text_status, @@ -1521,6 +1551,7 @@ tweak_statushash(void *baton, /* If not, make it so. */ if (! statstruct) { + svn_wc__internal_status_t *i_stat; /* If this item isn't being added, then we're most likely dealing with a non-recursive (or at least partially non-recursive) working copy. Due to bugs in how the client @@ -1536,8 +1567,9 @@ tweak_statushash(void *baton, return SVN_NO_ERROR; /* Use the public API to get a statstruct, and put it into the hash. */ - SVN_ERR(internal_status(&statstruct, db, local_abspath, pool, - scratch_pool)); + SVN_ERR(internal_status(&i_stat, db, local_abspath, + check_working_copy, pool, scratch_pool)); + statstruct = &i_stat->s; statstruct->repos_lock = repos_lock; svn_hash_sets(statushash, apr_pstrdup(pool, local_abspath), statstruct); } @@ -1576,9 +1608,9 @@ tweak_statushash(void *baton, statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath); statstruct->repos_root_url = - b->edit_baton->anchor_status->repos_root_url; + b->edit_baton->anchor_status->s.repos_root_url; statstruct->repos_uuid = - b->edit_baton->anchor_status->repos_uuid; + b->edit_baton->anchor_status->s.repos_uuid; } /* The last committed date, and author for deleted items @@ -1618,9 +1650,9 @@ tweak_statushash(void *baton, { statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath); statstruct->repos_root_url = - b->edit_baton->anchor_status->repos_root_url; + b->edit_baton->anchor_status->s.repos_root_url; statstruct->repos_uuid = - b->edit_baton->anchor_status->repos_uuid; + b->edit_baton->anchor_status->s.repos_uuid; } statstruct->ood_kind = b->ood_kind; if (b->ood_changed_author) @@ -1636,7 +1668,7 @@ find_dir_repos_relpath(const struct dir_baton *db, apr_pool_t *pool) { /* If we have no name, we're the root, return the anchor URL. */ if (! db->name) - return db->edit_baton->anchor_status->repos_relpath; + return db->edit_baton->anchor_status->s.repos_relpath; else { const char *repos_relpath; @@ -1668,7 +1700,7 @@ make_dir_baton(void **dir_baton, struct edit_baton *eb = edit_baton; struct dir_baton *d; const char *local_abspath; - const svn_wc_status3_t *status_in_parent; + const svn_wc__internal_status_t *status_in_parent; apr_pool_t *dir_pool; if (parent_baton) @@ -1727,8 +1759,7 @@ make_dir_baton(void **dir_baton, status_in_parent = eb->anchor_status; if (status_in_parent - && status_in_parent->versioned - && (status_in_parent->kind == svn_node_dir) + && (status_in_parent->has_descendants) && (! d->excluded) && (d->depth == svn_depth_unknown || d->depth == svn_depth_infinity @@ -1740,9 +1771,9 @@ make_dir_baton(void **dir_baton, const apr_array_header_t *ignores = eb->ignores; SVN_ERR(get_dir_status(&eb->wb, local_abspath, TRUE, - status_in_parent->repos_root_url, + status_in_parent->s.repos_root_url, NULL /*parent_repos_relpath*/, - status_in_parent->repos_uuid, + status_in_parent->s.repos_uuid, NULL, NULL /* dirent */, ignores, d->depth == svn_depth_files @@ -1757,7 +1788,7 @@ make_dir_baton(void **dir_baton, this_dir_status = svn_hash_gets(d->statii, d->local_abspath); if (this_dir_status && this_dir_status->versioned && (d->depth == svn_depth_unknown - || d->depth > status_in_parent->depth)) + || d->depth > status_in_parent->s.depth)) { d->depth = this_dir_status->depth; } @@ -1799,12 +1830,15 @@ make_file_baton(struct dir_baton *parent_dir_baton, * Return a boolean answer to the question "Is @a status something that * should be reported?". @a no_ignore and @a get_all are the same as * svn_wc_get_status_editor4(). + * + * This implementation should match the filter in assemble_status() */ static svn_boolean_t -is_sendable_status(const svn_wc_status3_t *status, +is_sendable_status(const svn_wc__internal_status_t *i_status, svn_boolean_t no_ignore, svn_boolean_t get_all) { + const svn_wc_status3_t *status = &i_status->s; /* If the repository status was touched at all, it's interesting. */ if (status->repos_node_status != svn_wc_status_none) return TRUE; @@ -1830,8 +1864,8 @@ is_sendable_status(const svn_wc_status3_t *status, return TRUE; /* If the text, property or tree state is interesting, send it. */ - if ((status->node_status != svn_wc_status_none - && (status->node_status != svn_wc_status_normal))) + if ((status->node_status != svn_wc_status_none) + && (status->node_status != svn_wc_status_normal)) return TRUE; /* If it's switched, send it. */ @@ -1846,6 +1880,9 @@ is_sendable_status(const svn_wc_status3_t *status, if (status->changelist) return TRUE; + if (status->moved_to_abspath) + return TRUE; + /* Otherwise, don't send it. */ return FALSE; } @@ -1910,15 +1947,15 @@ handle_statii(struct edit_baton *eb, /* Loop over all the statii still in our hash, handling each one. */ for (hi = apr_hash_first(pool, statii); hi; hi = apr_hash_next(hi)) { - const char *local_abspath = svn__apr_hash_index_key(hi); - svn_wc_status3_t *status = svn__apr_hash_index_val(hi); + const char *local_abspath = apr_hash_this_key(hi); + svn_wc__internal_status_t *status = apr_hash_this_val(hi); /* Clear the subpool. */ svn_pool_clear(iterpool); /* Now, handle the status. We don't recurse for svn_depth_immediates because we already have the subdirectories' statii. */ - if (status->versioned && status->kind == svn_node_dir + if (status->has_descendants && (depth == svn_depth_unknown || depth == svn_depth_infinity)) { @@ -1934,9 +1971,9 @@ handle_statii(struct edit_baton *eb, iterpool)); } if (dir_was_deleted) - status->repos_node_status = svn_wc_status_deleted; + status->s.repos_node_status = svn_wc_status_deleted; if (is_sendable_status(status, eb->no_ignore, eb->get_all)) - SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, status, + SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, &status->s, iterpool)); } @@ -1991,7 +2028,7 @@ delete_entry(const char *path, statushash immediately. No need to wait until close_file or close_dir, because there's no risk of having to honor the 'added' flag. We already know this item exists in the working copy. */ - SVN_ERR(tweak_statushash(db, db, TRUE, eb->db, + SVN_ERR(tweak_statushash(db, db, TRUE, eb->db, eb->wb.check_working_copy, local_abspath, svn_wc_status_deleted, 0, 0, revision, NULL, pool)); @@ -1999,7 +2036,8 @@ delete_entry(const char *path, is the root node and we're not supposed to report on the root node). */ if (db->parent_baton && (! *eb->target_basename)) - SVN_ERR(tweak_statushash(db->parent_baton, db, TRUE,eb->db, + SVN_ERR(tweak_statushash(db->parent_baton, db, TRUE, + eb->db, eb->wb.check_working_copy, db->local_abspath, svn_wc_status_modified, svn_wc_status_modified, 0, SVN_INVALID_REVNUM, NULL, pool)); @@ -2123,7 +2161,9 @@ close_directory(void *dir_baton, { /* ### When we add directory locking, we need to find a ### directory lock here. */ - SVN_ERR(tweak_statushash(pb, db, TRUE, eb->db, db->local_abspath, + SVN_ERR(tweak_statushash(pb, db, TRUE, + eb->db, eb->wb.check_working_copy, + db->local_abspath, repos_node_status, repos_text_status, repos_prop_status, SVN_INVALID_REVNUM, NULL, scratch_pool)); @@ -2133,17 +2173,17 @@ close_directory(void *dir_baton, /* We're editing the root dir of the WC. As its repos status info isn't otherwise set, set it directly to trigger invocation of the status callback below. */ - eb->anchor_status->repos_node_status = repos_node_status; - eb->anchor_status->repos_prop_status = repos_prop_status; - eb->anchor_status->repos_text_status = repos_text_status; + eb->anchor_status->s.repos_node_status = repos_node_status; + eb->anchor_status->s.repos_prop_status = repos_prop_status; + eb->anchor_status->s.repos_text_status = repos_text_status; /* If the root dir is out of date set the ood info directly too. */ - if (db->ood_changed_rev != eb->anchor_status->revision) + if (db->ood_changed_rev != eb->anchor_status->s.revision) { - eb->anchor_status->ood_changed_rev = db->ood_changed_rev; - eb->anchor_status->ood_changed_date = db->ood_changed_date; - eb->anchor_status->ood_kind = db->ood_kind; - eb->anchor_status->ood_changed_author = + eb->anchor_status->s.ood_changed_rev = db->ood_changed_rev; + eb->anchor_status->s.ood_changed_date = db->ood_changed_date; + eb->anchor_status->s.ood_kind = db->ood_kind; + eb->anchor_status->s.ood_changed_author = apr_pstrdup(pool, db->ood_changed_author); } } @@ -2154,25 +2194,25 @@ close_directory(void *dir_baton, if (pb && ! db->excluded) { svn_boolean_t was_deleted = FALSE; - const svn_wc_status3_t *dir_status; + svn_wc__internal_status_t *dir_status; /* See if the directory was deleted or replaced. */ dir_status = svn_hash_gets(pb->statii, db->local_abspath); if (dir_status && - ((dir_status->repos_node_status == svn_wc_status_deleted) - || (dir_status->repos_node_status == svn_wc_status_replaced))) + ((dir_status->s.repos_node_status == svn_wc_status_deleted) + || (dir_status->s.repos_node_status == svn_wc_status_replaced))) was_deleted = TRUE; /* Now do the status reporting. */ SVN_ERR(handle_statii(eb, - dir_status ? dir_status->repos_root_url : NULL, - dir_status ? dir_status->repos_relpath : NULL, - dir_status ? dir_status->repos_uuid : NULL, + dir_status ? dir_status->s.repos_root_url : NULL, + dir_status ? dir_status->s.repos_relpath : NULL, + dir_status ? dir_status->s.repos_uuid : NULL, db->statii, was_deleted, db->depth, scratch_pool)); if (dir_status && is_sendable_status(dir_status, eb->no_ignore, eb->get_all)) SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath, - dir_status, scratch_pool)); + &dir_status->s, scratch_pool)); svn_hash_sets(pb->statii, db->local_abspath, NULL); } else if (! pb) @@ -2181,13 +2221,12 @@ close_directory(void *dir_baton, target, we should only report the target. */ if (*eb->target_basename) { - const svn_wc_status3_t *tgt_status; + const svn_wc__internal_status_t *tgt_status; tgt_status = svn_hash_gets(db->statii, eb->target_abspath); if (tgt_status) { - if (tgt_status->versioned - && tgt_status->kind == svn_node_dir) + if (tgt_status->has_descendants) { SVN_ERR(get_dir_status(&eb->wb, eb->target_abspath, TRUE, @@ -2202,7 +2241,7 @@ close_directory(void *dir_baton, } if (is_sendable_status(tgt_status, eb->no_ignore, eb->get_all)) SVN_ERR((eb->status_func)(eb->status_baton, eb->target_abspath, - tgt_status, scratch_pool)); + &tgt_status->s, scratch_pool)); } } else @@ -2211,15 +2250,15 @@ close_directory(void *dir_baton, Note that our directory couldn't have been deleted, because it is the root of the edit drive. */ SVN_ERR(handle_statii(eb, - eb->anchor_status->repos_root_url, - eb->anchor_status->repos_relpath, - eb->anchor_status->repos_uuid, + eb->anchor_status->s.repos_root_url, + eb->anchor_status->s.repos_relpath, + eb->anchor_status->s.repos_uuid, db->statii, FALSE, eb->default_depth, scratch_pool)); if (is_sendable_status(eb->anchor_status, eb->no_ignore, eb->get_all)) SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath, - eb->anchor_status, scratch_pool)); + &eb->anchor_status->s, scratch_pool)); eb->anchor_status = NULL; } } @@ -2375,6 +2414,7 @@ close_file(void *file_baton, } return tweak_statushash(fb, NULL, FALSE, fb->edit_baton->db, + fb->edit_baton->wb.check_working_copy, fb->local_abspath, repos_node_status, repos_text_status, repos_prop_status, SVN_INVALID_REVNUM, repos_lock, pool); @@ -2393,18 +2433,18 @@ close_edit(void *edit_baton, if (eb->root_opened) return SVN_NO_ERROR; - SVN_ERR(svn_wc_walk_status(eb->wc_ctx, - eb->target_abspath, - eb->default_depth, - eb->get_all, - eb->no_ignore, - FALSE, - eb->ignores, - eb->status_func, - eb->status_baton, - eb->cancel_func, - eb->cancel_baton, - pool)); + SVN_ERR(svn_wc__internal_walk_status(eb->db, + eb->target_abspath, + eb->default_depth, + eb->get_all, + eb->no_ignore, + FALSE, + eb->ignores, + eb->status_func, + eb->status_baton, + eb->cancel_func, + eb->cancel_baton, + pool)); return SVN_NO_ERROR; } @@ -2423,6 +2463,7 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, const char *target_basename, svn_depth_t depth, svn_boolean_t get_all, + svn_boolean_t check_working_copy, svn_boolean_t no_ignore, svn_boolean_t depth_as_sticky, svn_boolean_t server_performs_filtering, @@ -2447,7 +2488,6 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, eb->default_depth = depth; eb->target_revision = edit_revision; eb->db = wc_ctx->db; - eb->wc_ctx = wc_ctx; eb->get_all = get_all; eb->no_ignore = no_ignore; eb->status_func = status_func; @@ -2463,7 +2503,8 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, eb->wb.db = wc_ctx->db; eb->wb.target_abspath = eb->target_abspath; - eb->wb.ignore_text_mods = FALSE; + eb->wb.ignore_text_mods = !check_working_copy; + eb->wb.check_working_copy = check_working_copy; eb->wb.repos_locks = NULL; eb->wb.repos_root = NULL; @@ -2488,7 +2529,7 @@ svn_wc__get_status_editor(const svn_delta_editor_t **editor, /* The edit baton's status structure maps to PATH, and the editor have to be aware of whether that is the anchor or the target. */ SVN_ERR(internal_status(&(eb->anchor_status), wc_ctx->db, anchor_abspath, - result_pool, scratch_pool)); + check_working_copy, result_pool, scratch_pool)); /* Construct an editor. */ tree_editor->set_target_revision = set_target_revision; @@ -2594,6 +2635,7 @@ svn_wc__internal_walk_status(svn_wc__db_t *db, wb.db = db; wb.target_abspath = local_abspath; wb.ignore_text_mods = ignore_text_mods; + wb.check_working_copy = TRUE; wb.repos_root = NULL; wb.repos_locks = NULL; @@ -2608,6 +2650,7 @@ svn_wc__internal_walk_status(svn_wc__db_t *db, } err = svn_wc__db_read_single_info(&info, db, local_abspath, + FALSE /* base_tree_only */, scratch_pool, scratch_pool); if (err) @@ -2636,7 +2679,7 @@ svn_wc__internal_walk_status(svn_wc__db_t *db, } if (info - && info->kind == svn_node_dir + && info->has_descendants /* is dir, or was dir and has tc descendants */ && info->status != svn_wc__db_status_not_present && info->status != svn_wc__db_status_excluded && info->status != svn_wc__db_status_server_excluded) @@ -2756,30 +2799,26 @@ svn_wc_get_default_ignores(apr_array_header_t **patterns, /* */ static svn_error_t * -internal_status(svn_wc_status3_t **status, +internal_status(svn_wc__internal_status_t **status, svn_wc__db_t *db, const char *local_abspath, + svn_boolean_t check_working_copy, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - const svn_io_dirent2_t *dirent; - svn_node_kind_t node_kind; + const svn_io_dirent2_t *dirent = NULL; const char *parent_repos_relpath; const char *parent_repos_root_url; const char *parent_repos_uuid; - svn_wc__db_status_t node_status; - svn_boolean_t conflicted; + const struct svn_wc__db_info_t *info; svn_boolean_t is_root = FALSE; svn_error_t *err; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - err = svn_wc__db_read_info(&node_status, &node_kind, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, &conflicted, - NULL, NULL, NULL, NULL, NULL, NULL, - db, local_abspath, - scratch_pool, scratch_pool); + err = svn_wc__db_read_single_info(&info, db, local_abspath, + !check_working_copy, + scratch_pool, scratch_pool); if (err) { @@ -2787,30 +2826,25 @@ internal_status(svn_wc_status3_t **status, return svn_error_trace(err); svn_error_clear(err); - node_kind = svn_node_unknown; - /* Ensure conflicted is always set, but don't hide tree conflicts - on 'hidden' nodes. */ - conflicted = FALSE; + info = NULL; - SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE, - scratch_pool, scratch_pool)); + if (check_working_copy) + SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE, + scratch_pool, scratch_pool)); } - else + else if (check_working_copy) SVN_ERR(stat_wc_dirent_case_sensitive(&dirent, db, local_abspath, scratch_pool, scratch_pool)); - if (node_kind != svn_node_unknown - && (node_status == svn_wc__db_status_not_present - || node_status == svn_wc__db_status_server_excluded - || node_status == svn_wc__db_status_excluded)) - { - node_kind = svn_node_unknown; - } - - if (node_kind == svn_node_unknown) + if (!info + || info->kind == svn_node_unknown + || info->status == svn_wc__db_status_not_present + || info->status == svn_wc__db_status_server_excluded + || info->status == svn_wc__db_status_excluded) return svn_error_trace(assemble_unversioned(status, db, local_abspath, - dirent, conflicted, + dirent, + info ? info->conflicted : FALSE, FALSE /* is_ignored */, result_pool, scratch_pool)); @@ -2819,30 +2853,31 @@ internal_status(svn_wc_status3_t **status, else SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool)); + /* Even though passing parent_repos_* is not required, assemble_status needs + these values to determine if a node is switched */ if (!is_root) { - svn_wc__db_status_t parent_status; - const char *parent_abspath = svn_dirent_dirname(local_abspath, - scratch_pool); - - err = svn_wc__db_read_info(&parent_status, NULL, NULL, - &parent_repos_relpath, &parent_repos_root_url, - &parent_repos_uuid, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - db, parent_abspath, - result_pool, scratch_pool); - - if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND - || SVN_WC__ERR_IS_NOT_CURRENT_WC(err))) - { - svn_error_clear(err); - parent_repos_root_url = NULL; - parent_repos_relpath = NULL; - parent_repos_uuid = NULL; - } - else SVN_ERR(err); + const char *const parent_abspath = svn_dirent_dirname(local_abspath, + scratch_pool); + if (check_working_copy) + SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, + &parent_repos_relpath, + &parent_repos_root_url, + &parent_repos_uuid, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + db, parent_abspath, + result_pool, scratch_pool)); + else + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, + &parent_repos_relpath, + &parent_repos_root_url, + &parent_repos_uuid, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + db, parent_abspath, + result_pool, scratch_pool)); } else { @@ -2855,10 +2890,10 @@ internal_status(svn_wc_status3_t **status, parent_repos_root_url, parent_repos_relpath, parent_repos_uuid, - NULL, + info, dirent, TRUE /* get_all */, - FALSE, + FALSE, check_working_copy, NULL /* repos_lock */, result_pool, scratch_pool)); } @@ -2871,16 +2906,21 @@ svn_wc_status3(svn_wc_status3_t **status, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - return svn_error_trace( - internal_status(status, wc_ctx->db, local_abspath, result_pool, - scratch_pool)); + svn_wc__internal_status_t *stat; + SVN_ERR(internal_status(&stat, wc_ctx->db, local_abspath, + TRUE /* check_working_copy */, + result_pool, scratch_pool)); + *status = &stat->s; + return SVN_NO_ERROR; } svn_wc_status3_t * svn_wc_dup_status3(const svn_wc_status3_t *orig_stat, apr_pool_t *pool) { - svn_wc_status3_t *new_stat = apr_palloc(pool, sizeof(*new_stat)); + /* Allocate slightly more room */ + svn_wc__internal_status_t *new_istat = apr_palloc(pool, sizeof(*new_istat)); + svn_wc_status3_t *new_stat = &new_istat->s; /* Shallow copy all members. */ *new_stat = *orig_stat; diff --git a/subversion/libsvn_wc/token-map.h b/subversion/libsvn_wc/token-map.h index 9da12b8..9bf80d6 100644 --- a/subversion/libsvn_wc/token-map.h +++ b/subversion/libsvn_wc/token-map.h @@ -33,6 +33,7 @@ extern "C" { #endif +/* The kind values used on NODES */ static const svn_token_map_t kind_map[] = { { "file", svn_node_file }, /* MAP_FILE */ { "dir", svn_node_dir }, /* MAP_DIR */ @@ -41,6 +42,16 @@ static const svn_token_map_t kind_map[] = { { NULL } }; +/* Like kind_map, but also supports 'none' */ +static const svn_token_map_t kind_map_none[] = { + { "none", svn_node_none }, + { "file", svn_node_file }, + { "dir", svn_node_dir }, + { "symlink", svn_node_symlink }, + { "unknown", svn_node_unknown }, + { NULL } +}; + /* Note: we only decode presence values from the database. These are a subset of all the status values. */ static const svn_token_map_t presence_map[] = { diff --git a/subversion/libsvn_wc/translate.c b/subversion/libsvn_wc/translate.c index 9e0b265..0e16235 100644 --- a/subversion/libsvn_wc/translate.c +++ b/subversion/libsvn_wc/translate.c @@ -30,6 +30,7 @@ #include <apr_file_io.h> #include <apr_strings.h> +#include "svn_private_config.h" #include "svn_types.h" #include "svn_string.h" #include "svn_dirent_uri.h" @@ -45,25 +46,9 @@ #include "translate.h" #include "props.h" -#include "svn_private_config.h" #include "private/svn_wc_private.h" - -/* */ -static svn_error_t * -read_handler_unsupported(void *baton, char *buffer, apr_size_t *len) -{ - SVN_ERR_MALFUNCTION(); -} - -/* */ -static svn_error_t * -write_handler_unsupported(void *baton, const char *buffer, apr_size_t *len) -{ - SVN_ERR_MALFUNCTION(); -} - svn_error_t * svn_wc__internal_translated_stream(svn_stream_t **stream, svn_wc__db_t *db, @@ -133,16 +118,18 @@ svn_wc__internal_translated_stream(svn_stream_t **stream, FALSE /* expand */, result_pool); - /* Enforce our contract. TO_NF streams are readonly */ - svn_stream_set_write(*stream, write_handler_unsupported); + /* streams enforce our contract that TO_NF streams are read-only + * by returning SVN_ERR_STREAM_NOT_SUPPORTED when trying to + * write to them. */ } else { *stream = svn_subst_stream_translated(*stream, eol, TRUE, keywords, TRUE, result_pool); - /* Enforce our contract. FROM_NF streams are write-only */ - svn_stream_set_read(*stream, read_handler_unsupported); + /* streams enforce our contract that FROM_NF streams are write-only + * by returning SVN_ERR_STREAM_NOT_SUPPORTED when trying to + * read them. */ } } @@ -329,12 +316,15 @@ svn_wc__expand_keywords(apr_hash_t **keywords, db, local_abspath, scratch_pool, scratch_pool)); - if (repos_relpath) - url = svn_path_url_add_component2(repos_root_url, repos_relpath, - scratch_pool); - else - SVN_ERR(svn_wc__db_read_url(&url, db, local_abspath, scratch_pool, - scratch_pool)); + /* Handle special statuses (e.g. added) */ + if (!repos_relpath) + SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, + &repos_root_url, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + url = svn_path_url_add_component2(repos_root_url, repos_relpath, + scratch_pool); } else { diff --git a/subversion/libsvn_wc/tree_conflicts.c b/subversion/libsvn_wc/tree_conflicts.c index 4445c96..caf39ed 100644 --- a/subversion/libsvn_wc/tree_conflicts.c +++ b/subversion/libsvn_wc/tree_conflicts.c @@ -46,6 +46,7 @@ static const svn_token_map_t node_kind_map[] = { "file", svn_node_file }, { "dir", svn_node_dir }, { "", svn_node_unknown }, + /* ### should also map svn_node_symlink */ { NULL } }; @@ -353,13 +354,13 @@ svn_wc__serialize_conflict(svn_skel_t **skel, else SVN_ERR(prepend_version_info_skel(c_skel, &null_version, result_pool)); - /* reason */ - skel_prepend_enum(c_skel, svn_wc__conflict_reason_map, conflict->reason, - result_pool); + /* local change */ + skel_prepend_enum(c_skel, svn_wc__conflict_reason_map, + conflict->reason, result_pool); - /* action */ - skel_prepend_enum(c_skel, svn_wc__conflict_action_map, conflict->action, - result_pool); + /* incoming change */ + skel_prepend_enum(c_skel, svn_wc__conflict_action_map, + conflict->action, result_pool); /* operation */ skel_prepend_enum(c_skel, svn_wc__operation_map, conflict->operation, @@ -367,8 +368,10 @@ svn_wc__serialize_conflict(svn_skel_t **skel, /* node_kind */ SVN_ERR_ASSERT(conflict->node_kind == svn_node_dir - || conflict->node_kind == svn_node_file); - skel_prepend_enum(c_skel, node_kind_map, conflict->node_kind, result_pool); + || conflict->node_kind == svn_node_file + || conflict->node_kind == svn_node_none); + skel_prepend_enum(c_skel, node_kind_map, conflict->node_kind, + result_pool); /* Victim path (escaping separator chars). */ victim_basename = svn_dirent_basename(conflict->local_abspath, result_pool); @@ -409,10 +412,9 @@ svn_wc__add_tree_conflict(svn_wc_context_t *wc_ctx, svn_error_t *err; SVN_ERR_ASSERT(conflict != NULL); - SVN_ERR_ASSERT(conflict->operation == svn_wc_operation_merge - || (conflict->reason != svn_wc_conflict_reason_moved_away - && conflict->reason != svn_wc_conflict_reason_moved_here) - ); + SVN_ERR_ASSERT(conflict->operation == svn_wc_operation_merge || + (conflict->reason != svn_wc_conflict_reason_moved_away && + conflict->reason != svn_wc_conflict_reason_moved_here)); /* Re-adding an existing tree conflict victim is an error. */ err = svn_wc__internal_conflicted_p(NULL, NULL, &existing_conflict, @@ -443,7 +445,7 @@ svn_wc__add_tree_conflict(svn_wc_context_t *wc_ctx, NULL, scratch_pool, scratch_pool)); - switch(conflict->operation) + switch (conflict->operation) { case svn_wc_operation_update: default: @@ -483,8 +485,10 @@ svn_wc__get_tree_conflict(const svn_wc_conflict_description2_t **tree_conflict, int i; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - SVN_ERR(svn_wc__read_conflicts(&conflicts, - wc_ctx->db, local_abspath, FALSE, + SVN_ERR(svn_wc__read_conflicts(&conflicts, NULL, + wc_ctx->db, local_abspath, + FALSE /* temp files */, + TRUE /* only tree conflicts */, scratch_pool, scratch_pool)); if (!conflicts || conflicts->nelts == 0) @@ -501,8 +505,7 @@ svn_wc__get_tree_conflict(const svn_wc_conflict_description2_t **tree_conflict, if (desc->kind == svn_wc_conflict_kind_tree) { - *tree_conflict = svn_wc__conflict_description2_dup(desc, - result_pool); + *tree_conflict = svn_wc_conflict_description2_dup(desc, result_pool); return SVN_NO_ERROR; } } diff --git a/subversion/libsvn_wc/update_editor.c b/subversion/libsvn_wc/update_editor.c index fd3e9ca..4dca3af 100644 --- a/subversion/libsvn_wc/update_editor.c +++ b/subversion/libsvn_wc/update_editor.c @@ -211,7 +211,7 @@ struct edit_baton /* If this is a 'switch' operation, the new relpath of target_abspath, else NULL. */ - const char *switch_relpath; + const char *switch_repos_relpath; /* The URL to the root of the repository. */ const char *repos_root; @@ -258,6 +258,9 @@ struct edit_baton /* Absolute path of the working copy root or NULL if not initialized yet */ const char *wcroot_abspath; + /* After closing the root directory a copy of its edited value */ + svn_boolean_t edited; + apr_pool_t *pool; }; @@ -295,7 +298,7 @@ struct dir_baton const char *local_abspath; /* The repository relative path this directory will correspond to. */ - const char *new_relpath; + const char *new_repos_relpath; /* The revision of the directory before updating */ svn_revnum_t old_revision; @@ -342,9 +345,10 @@ struct dir_baton and reinstall it. */ apr_hash_t *deletion_conflicts; - /* A hash of file names (only the hash key matters) seen by add_file - and not yet added to the database by close_file. */ - apr_hash_t *not_present_files; + /* A hash of file names (only the hash key matters) seen by add_file and + add_directory and not yet added to the database, mapping to a const + char * node kind (via svn_node_kind_to_word(). */ + apr_hash_t *not_present_nodes; /* Set if an unversioned dir of the same name already existed in this directory. */ @@ -398,7 +402,7 @@ struct handler_baton struct file_baton *fb; /* Where we are assembling the new file. */ - const char *new_text_base_tmp_abspath; + svn_wc__db_install_data_t *install_data; /* The expected source checksum of the text source or NULL if no base checksum is available (MD5 if the server provides a checksum, SHA1 if @@ -412,7 +416,7 @@ struct handler_baton provide a sha1, so we may not have to calculate both, but for the time being, that's the way it is. */ - /* The calculated checksum of the text source or NULL if the acual + /* The calculated checksum of the text source or NULL if the actual checksum is not being calculated. The checksum kind is identical to the kind of expected_source_checksum. */ svn_checksum_t *actual_source_checksum; @@ -486,47 +490,20 @@ cleanup_edit_baton(void *edit_baton) return APR_SUCCESS; } -/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton. - If PATH and PB are NULL, this is the root directory of the edit; in this - case, make the new dir baton in a subpool of EB->pool. - ADDING should be TRUE if we are adding this directory. */ +/* Calculate the new repos_relpath for a directory or file */ static svn_error_t * -make_dir_baton(struct dir_baton **d_p, - const char *path, - struct edit_baton *eb, - struct dir_baton *pb, - svn_boolean_t adding, - apr_pool_t *scratch_pool) +calculate_repos_relpath(const char **new_repos_relpath, + const char *local_abspath, + const char *old_repos_relpath, + struct edit_baton *eb, + struct dir_baton *pb, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_pool_t *dir_pool; - struct dir_baton *d; - - if (pb != NULL) - dir_pool = svn_pool_create(pb->pool); - else - dir_pool = svn_pool_create(eb->pool); - - SVN_ERR_ASSERT(path || (! pb)); - - /* Okay, no easy out, so allocate and initialize a dir baton. */ - d = apr_pcalloc(dir_pool, sizeof(*d)); + const char *name = svn_dirent_basename(local_abspath, NULL); - /* Construct the PATH and baseNAME of this directory. */ - if (path) - { - d->name = svn_dirent_basename(path, dir_pool); - SVN_ERR(path_join_under_root(&d->local_abspath, - pb->local_abspath, d->name, dir_pool)); - } - else - { - /* This is the root baton. */ - d->name = NULL; - d->local_abspath = eb->anchor_abspath; - } - - /* Figure out the new_relpath for this directory. */ - if (eb->switch_relpath) + /* Figure out the new_repos_relpath for this directory. */ + if (eb->switch_repos_relpath) { /* Handle switches... */ @@ -535,18 +512,16 @@ make_dir_baton(struct dir_baton **d_p, if (*eb->target_basename == '\0') { /* No parent baton and target_basename=="" means that we are - the target of the switch. Thus, our NEW_RELPATH will be - the SWITCH_RELPATH. */ - d->new_relpath = eb->switch_relpath; + the target of the switch. Thus, our new_repos_relpath will be + the switch_repos_relpath. */ + *new_repos_relpath = eb->switch_repos_relpath; } else { /* This node is NOT the target of the switch (one of our children is the target); therefore, it must already exist. Get its old REPOS_RELPATH, as it won't be changing. */ - SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL, - eb->db, d->local_abspath, - dir_pool, scratch_pool)); + *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath); } } else @@ -554,36 +529,76 @@ make_dir_baton(struct dir_baton **d_p, /* This directory is *not* the root (has a parent). If there is no grandparent, then we may have anchored at the parent, and self is the target. If we match the target, then set - NEW_RELPATH to the SWITCH_RELPATH. + new_repos_relpath to the switch_repos_relpath. + + Otherwise, we simply extend new_repos_relpath from the parent. */ - Otherwise, we simply extend NEW_RELPATH from the parent. */ if (pb->parent_baton == NULL - && strcmp(eb->target_basename, d->name) == 0) - d->new_relpath = eb->switch_relpath; + && strcmp(eb->target_basename, name) == 0) + *new_repos_relpath = eb->switch_repos_relpath; else - d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, - dir_pool); + *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, + result_pool); } } else /* must be an update */ { /* If we are adding the node, then simply extend the parent's relpath for our own. */ - if (adding) + if (old_repos_relpath == NULL) { SVN_ERR_ASSERT(pb != NULL); - d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, - dir_pool); + *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, + result_pool); } else { - SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL, - eb->db, d->local_abspath, - dir_pool, scratch_pool)); - SVN_ERR_ASSERT(d->new_relpath); + *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath); } } + return SVN_NO_ERROR; +} + +/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton. + If PATH and PB are NULL, this is the root directory of the edit; in this + case, make the new dir baton in a subpool of EB->pool. + ADDING should be TRUE if we are adding this directory. */ +static svn_error_t * +make_dir_baton(struct dir_baton **d_p, + const char *path, + struct edit_baton *eb, + struct dir_baton *pb, + svn_boolean_t adding, + apr_pool_t *scratch_pool) +{ + apr_pool_t *dir_pool; + struct dir_baton *d; + + if (pb != NULL) + dir_pool = svn_pool_create(pb->pool); + else + dir_pool = svn_pool_create(eb->pool); + + SVN_ERR_ASSERT(path || (! pb)); + + /* Okay, no easy out, so allocate and initialize a dir baton. */ + d = apr_pcalloc(dir_pool, sizeof(*d)); + + /* Construct the PATH and baseNAME of this directory. */ + if (path) + { + d->name = svn_dirent_basename(path, dir_pool); + SVN_ERR(path_join_under_root(&d->local_abspath, + pb->local_abspath, d->name, dir_pool)); + } + else + { + /* This is the root baton. */ + d->name = NULL; + d->local_abspath = eb->anchor_abspath; + } + d->edit_baton = eb; d->parent_baton = pb; d->pool = dir_pool; @@ -594,7 +609,7 @@ make_dir_baton(struct dir_baton **d_p, d->old_revision = SVN_INVALID_REVNUM; d->adding_dir = adding; d->changed_rev = SVN_INVALID_REVNUM; - d->not_present_files = apr_hash_make(dir_pool); + d->not_present_nodes = apr_hash_make(dir_pool); /* Copy some flags from the parent baton */ if (pb) @@ -614,7 +629,6 @@ make_dir_baton(struct dir_baton **d_p, return SVN_NO_ERROR; } - /* Forward declarations. */ static svn_error_t * already_in_a_tree_conflict(svn_boolean_t *conflicted, @@ -680,7 +694,7 @@ struct file_baton const char *local_abspath; /* The repository relative path this file will correspond to. */ - const char *new_relpath; + const char *new_repos_relpath; /* The revision of the file before updating */ svn_revnum_t old_revision; @@ -764,7 +778,6 @@ make_file_baton(struct file_baton **f_p, svn_boolean_t adding, apr_pool_t *scratch_pool) { - struct edit_baton *eb = pb->edit_baton; apr_pool_t *file_pool = svn_pool_create(pb->pool); struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f)); @@ -776,37 +789,6 @@ make_file_baton(struct file_baton **f_p, SVN_ERR(path_join_under_root(&f->local_abspath, pb->local_abspath, f->name, file_pool)); - /* Figure out the new URL for this file. */ - if (eb->switch_relpath) - { - /* Handle switches... */ - - /* This file has a parent directory. If there is - no grandparent, then we may have anchored at the parent, - and self is the target. If we match the target, then set - NEW_RELPATH to the SWITCH_RELPATH. - - Otherwise, we simply extend NEW_RELPATH from the parent. */ - if (pb->parent_baton == NULL - && strcmp(eb->target_basename, f->name) == 0) - f->new_relpath = eb->switch_relpath; - else - f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, - file_pool); - } - else /* must be an update */ - { - if (adding) - f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool); - else - { - SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL, - eb->db, f->local_abspath, - file_pool, scratch_pool)); - SVN_ERR_ASSERT(f->new_relpath); - } - } - f->pool = file_pool; f->edit_baton = pb->edit_baton; f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t)); @@ -843,13 +825,16 @@ complete_conflict(svn_skel_t *conflict, const char *new_repos_relpath, svn_node_kind_t local_kind, svn_node_kind_t target_kind, + const svn_skel_t *delete_conflict, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_wc_conflict_version_t *original_version; + const svn_wc_conflict_version_t *original_version = NULL; svn_wc_conflict_version_t *target_version; svn_boolean_t is_complete; + SVN_ERR_ASSERT(new_repos_relpath); + if (!conflict) return SVN_NO_ERROR; /* Not conflicted */ @@ -865,20 +850,30 @@ complete_conflict(svn_skel_t *conflict, old_revision, local_kind, result_pool); - else - original_version = NULL; + else if (delete_conflict) + { + const apr_array_header_t *locations; - if (new_repos_relpath) - target_version = svn_wc_conflict_version_create2(eb->repos_root, - eb->repos_uuid, - new_repos_relpath, - *eb->target_revision, - target_kind, - result_pool); - else - target_version = NULL; + SVN_ERR(svn_wc__conflict_read_info(NULL, &locations, NULL, NULL, NULL, + eb->db, local_abspath, + delete_conflict, + scratch_pool, scratch_pool)); + + if (locations) + { + original_version = APR_ARRAY_IDX(locations, 0, + const svn_wc_conflict_version_t *); + } + } - if (eb->switch_relpath) + target_version = svn_wc_conflict_version_create2(eb->repos_root, + eb->repos_uuid, + new_repos_relpath, + *eb->target_revision, + target_kind, + result_pool); + + if (eb->switch_repos_relpath) SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict, original_version, target_version, @@ -913,8 +908,9 @@ mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool) SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton, db->local_abspath, db->old_repos_relpath, db->old_revision, - db->new_relpath, + db->new_repos_relpath, svn_node_dir, svn_node_dir, + NULL, db->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db, db->local_abspath, @@ -924,7 +920,6 @@ mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool) do_notification(db->edit_baton, db->local_abspath, svn_node_dir, svn_wc_notify_tree_conflict, scratch_pool); db->already_notified = TRUE; - } return SVN_NO_ERROR; @@ -948,8 +943,9 @@ mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool) SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, fb->local_abspath, fb->old_repos_relpath, - fb->old_revision, fb->new_relpath, + fb->old_revision, fb->new_repos_relpath, svn_node_file, svn_node_file, + NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db, @@ -974,7 +970,6 @@ window_handler(svn_txdelta_window_t *window, void *baton) { struct handler_baton *hb = baton; struct file_baton *fb = hb->fb; - svn_wc__db_t *db = fb->edit_baton->db; svn_error_t *err; /* Apply this window. We may be done at that point. */ @@ -1014,10 +1009,10 @@ window_handler(svn_txdelta_window_t *window, void *baton) { /* We failed to apply the delta; clean up the temporary file if it already created by lazy_open_target(). */ - if (hb->new_text_base_tmp_abspath) + if (hb->install_data) { - svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, - TRUE, hb->pool)); + svn_error_clear(svn_wc__db_pristine_install_abort(hb->install_data, + hb->pool)); } } else @@ -1031,7 +1026,7 @@ window_handler(svn_txdelta_window_t *window, void *baton) /* Store the new pristine text in the pristine store now. Later, in a single transaction we will update the BASE_NODE to include a reference to this pristine text's checksum. */ - SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath, + SVN_ERR(svn_wc__db_pristine_install(hb->install_data, fb->new_text_base_sha1_checksum, fb->new_text_base_md5_checksum, hb->pool)); @@ -1154,17 +1149,6 @@ set_target_revision(void *edit_baton, return SVN_NO_ERROR; } -static svn_error_t * -check_tree_conflict(svn_skel_t **pconflict, - struct edit_baton *eb, - const char *local_abspath, - svn_wc__db_status_t working_status, - svn_boolean_t exists_in_repos, - svn_node_kind_t expected_kind, - svn_wc_conflict_action_t action, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - /* An svn_delta_editor_t function. */ static svn_error_t * open_root(void *edit_baton, @@ -1228,10 +1212,27 @@ open_root(void *edit_baton, eb->db, db->local_abspath, db->pool, pool)); - if (conflict_ignored) + if (have_work) { - db->shadowed = TRUE; + SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, + &db->old_revision, + &db->old_repos_relpath, NULL, NULL, + &db->changed_rev, &db->changed_date, + &db->changed_author, + &db->ambient_depth, + NULL, NULL, NULL, NULL, NULL, NULL, + eb->db, db->local_abspath, + db->pool, pool)); } + else + base_status = status; + + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + db->old_repos_relpath, eb, NULL, + db->pool, pool)); + + if (conflict_ignored) + db->shadowed = TRUE; else if (have_work) { const char *move_src_root_abspath; @@ -1239,16 +1240,6 @@ open_root(void *edit_baton, SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath, NULL, eb->db, db->local_abspath, pool, pool)); - if (move_src_root_abspath || *eb->target_basename == '\0') - SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, - &db->old_revision, - &db->old_repos_relpath, NULL, NULL, - &db->changed_rev, &db->changed_date, - &db->changed_author, - &db->ambient_depth, - NULL, NULL, NULL, NULL, NULL, NULL, - eb->db, db->local_abspath, - db->pool, pool)); if (move_src_root_abspath) { @@ -1271,9 +1262,10 @@ open_root(void *edit_baton, SVN_ERR(complete_conflict(tree_conflict, eb, move_src_root_abspath, db->old_repos_relpath, - db->old_revision, db->new_relpath, + db->old_revision, + db->new_repos_relpath, svn_node_dir, svn_node_dir, - pool, pool)); + NULL, pool, pool)); SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, move_src_root_abspath, tree_conflict, @@ -1288,8 +1280,6 @@ open_root(void *edit_baton, db->shadowed = TRUE; /* Needed for the close_directory() on the root, to make sure it doesn't use the ACTUAL tree */ } - else - base_status = status; if (*eb->target_basename == '\0') { @@ -1308,7 +1298,7 @@ open_root(void *edit_baton, SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath, - db->new_relpath, + db->new_repos_relpath, *eb->target_revision, pool)); } @@ -1320,99 +1310,6 @@ open_root(void *edit_baton, /* ===================================================================== */ /* Checking for local modifications. */ -/* A baton for use with modcheck_found_entry(). */ -typedef struct modcheck_baton_t { - svn_wc__db_t *db; /* wc_db to access nodes */ - svn_boolean_t found_mod; /* whether a modification has been found */ - svn_boolean_t found_not_delete; /* Found a not-delete modification */ -} modcheck_baton_t; - -/* An implementation of svn_wc_status_func4_t. */ -static svn_error_t * -modcheck_callback(void *baton, - const char *local_abspath, - const svn_wc_status3_t *status, - apr_pool_t *scratch_pool) -{ - modcheck_baton_t *mb = baton; - - switch (status->node_status) - { - case svn_wc_status_normal: - case svn_wc_status_incomplete: - case svn_wc_status_ignored: - case svn_wc_status_none: - case svn_wc_status_unversioned: - case svn_wc_status_external: - break; - - case svn_wc_status_deleted: - mb->found_mod = TRUE; - break; - - case svn_wc_status_missing: - case svn_wc_status_obstructed: - mb->found_mod = TRUE; - mb->found_not_delete = TRUE; - /* Exit from the status walker: We know what we want to know */ - return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); - - default: - case svn_wc_status_added: - case svn_wc_status_replaced: - case svn_wc_status_modified: - mb->found_mod = TRUE; - mb->found_not_delete = TRUE; - /* Exit from the status walker: We know what we want to know */ - return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); - } - - return SVN_NO_ERROR; -} - - -/* Set *MODIFIED to true iff there are any local modifications within the - * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED - * is set to true and all the local modifications were deletes then set - * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH - * may be a file or a directory. */ -svn_error_t * -svn_wc__node_has_local_mods(svn_boolean_t *modified, - svn_boolean_t *all_edits_are_deletes, - svn_wc__db_t *db, - const char *local_abspath, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE }; - svn_error_t *err; - - modcheck_baton.db = db; - - /* Walk the WC tree for status with depth infinity, looking for any local - * modifications. If it's a "sparse" directory, that's OK: there can be - * no local mods in the pieces that aren't present in the WC. */ - - err = svn_wc__internal_walk_status(db, local_abspath, - svn_depth_infinity, - FALSE, FALSE, FALSE, NULL, - modcheck_callback, &modcheck_baton, - cancel_func, cancel_baton, - scratch_pool); - - if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) - svn_error_clear(err); - else - SVN_ERR(err); - - *modified = modcheck_baton.found_mod; - *all_edits_are_deletes = (modcheck_baton.found_mod - && !modcheck_baton.found_not_delete); - - return SVN_NO_ERROR; -} - /* Indicates an unset svn_wc_conflict_reason_t. */ #define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1) @@ -1447,7 +1344,6 @@ check_tree_conflict(svn_skel_t **pconflict, { svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE; svn_boolean_t modified = FALSE; - svn_boolean_t all_mods_are_deletes = FALSE; const char *move_src_op_root_abspath = NULL; *pconflict = NULL; @@ -1486,15 +1382,15 @@ check_tree_conflict(svn_skel_t **pconflict, } else { - /* The node is locally replaced but could also be moved-away. */ - SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL, - &move_src_op_root_abspath, - eb->db, local_abspath, - scratch_pool, scratch_pool)); - if (move_src_op_root_abspath) - reason = svn_wc_conflict_reason_moved_away; - else - reason = svn_wc_conflict_reason_replaced; + /* The node is locally replaced but could also be moved-away, + but we can't report that it is moved away and replaced. + + And we wouldn't be able to store that each of a dozen + descendants was moved to other locations... + + Replaced is what actually happened... */ + + reason = svn_wc_conflict_reason_replaced; } break; @@ -1557,14 +1453,14 @@ check_tree_conflict(svn_skel_t **pconflict, * not visit the subdirectories of a directory that it wants to delete. * Therefore, we need to start a separate crawl here. */ - SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes, - eb->db, local_abspath, + SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL, + eb->db, local_abspath, FALSE, eb->cancel_func, eb->cancel_baton, scratch_pool)); if (modified) { - if (all_mods_are_deletes) + if (working_status == svn_wc__db_status_deleted) reason = svn_wc_conflict_reason_deleted; else reason = svn_wc_conflict_reason_edited; @@ -1711,7 +1607,8 @@ delete_entry(const char *path, const char *base = svn_relpath_basename(path, NULL); const char *local_abspath; const char *repos_relpath; - svn_node_kind_t kind, base_kind; + const char *deleted_repos_relpath; + svn_node_kind_t kind; svn_revnum_t old_revision; svn_boolean_t conflicted; svn_boolean_t have_work; @@ -1721,8 +1618,6 @@ delete_entry(const char *path, apr_pool_t *scratch_pool; svn_boolean_t deleting_target; svn_boolean_t deleting_switched; - svn_boolean_t keep_as_working = FALSE; - svn_boolean_t queue_deletes = TRUE; if (pb->skip_this) return SVN_NO_ERROR; @@ -1740,6 +1635,7 @@ delete_entry(const char *path, { svn_boolean_t is_root; + SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath, scratch_pool)); @@ -1767,10 +1663,9 @@ delete_entry(const char *path, if (!have_work) { base_status = status; - base_kind = kind; } else - SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision, + SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &old_revision, &repos_relpath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1815,11 +1710,9 @@ delete_entry(const char *path, || base_status == svn_wc__db_status_excluded || base_status == svn_wc__db_status_server_excluded) { - SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - FALSE /* keep_as_working */, - FALSE /* queue_deletes */, - FALSE /* remove_locks */, - SVN_INVALID_REVNUM /* not_present_rev */, + SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE, + deleting_target, FALSE, + *eb->target_revision, NULL, NULL, scratch_pool)); @@ -1842,18 +1735,13 @@ delete_entry(const char *path, { SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, status, TRUE, - (kind == svn_node_dir) - ? svn_node_dir - : svn_node_file, + kind, svn_wc_conflict_action_delete, pb->pool, scratch_pool)); } - else - queue_deletes = FALSE; /* There is no in-wc representation */ if (tree_conflict != NULL) { - svn_wc_conflict_reason_t reason; /* When we raise a tree conflict on a node, we don't want to mark the * node as skipped, to allow a replacement to continue doing at least * a bit of its work (possibly adding a not present node, for the @@ -1864,45 +1752,19 @@ delete_entry(const char *path, svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base), tree_conflict); - SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, - eb->db, local_abspath, - tree_conflict, - scratch_pool, scratch_pool)); - - if (reason == svn_wc_conflict_reason_edited - || reason == svn_wc_conflict_reason_obstructed) - { - /* The item exists locally and has some sort of local mod. - * It no longer exists in the repository at its target URL@REV. - * - * To prepare the "accept mine" resolution for the tree conflict, - * we must schedule the existing content for re-addition as a copy - * of what it was, but with its local modifications preserved. */ - keep_as_working = TRUE; - - /* Fall through to remove the BASE_NODEs properly, with potentially - keeping a not-present marker */ - } - else if (reason == svn_wc_conflict_reason_deleted - || reason == svn_wc_conflict_reason_moved_away - || reason == svn_wc_conflict_reason_replaced) - { - /* The item does not exist locally because it was already shadowed. - * We must complete the deletion, leaving the tree conflict info - * as the only difference from a normal deletion. */ - - /* Fall through to the normal "delete" code path. */ - } - else - SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */ + /* Whatever the kind of conflict, we can just clear BASE + by turning whatever is there into a copy */ } + /* Calculate the repository-relative path of the entry which was + * deleted. For updates it's the same as REPOS_RELPATH but for + * switches it is within the switch target. */ + SVN_ERR(calculate_repos_relpath(&deleted_repos_relpath, local_abspath, + repos_relpath, eb, pb, scratch_pool, + scratch_pool)); SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath, - old_revision, NULL, - (kind == svn_node_dir) - ? svn_node_dir - : svn_node_file, - svn_node_none, + old_revision, deleted_repos_relpath, + kind, svn_node_none, NULL, pb->pool, scratch_pool)); /* Issue a wq operation to delete the BASE_NODE data and to delete actual @@ -1917,7 +1779,8 @@ delete_entry(const char *path, { /* Delete, and do not leave a not-present node. */ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - keep_as_working, queue_deletes, FALSE, + (tree_conflict != NULL), + FALSE, FALSE, SVN_INVALID_REVNUM /* not_present_rev */, tree_conflict, NULL, scratch_pool)); @@ -1926,7 +1789,8 @@ delete_entry(const char *path, { /* Delete, leaving a not-present node. */ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, - keep_as_working, queue_deletes, FALSE, + (tree_conflict != NULL), + TRUE, FALSE, *eb->target_revision, tree_conflict, NULL, scratch_pool)); @@ -1948,6 +1812,7 @@ delete_entry(const char *path, { if (eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, + kind, tree_conflict, NULL /* merge_options */, eb->conflict_func, @@ -1955,23 +1820,17 @@ delete_entry(const char *path, eb->cancel_func, eb->cancel_baton, scratch_pool)); - do_notification(eb, local_abspath, svn_node_unknown, + do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict, scratch_pool); } else { svn_wc_notify_action_t action = svn_wc_notify_update_delete; - svn_node_kind_t node_kind; if (pb->shadowed || pb->edit_obstructed) action = svn_wc_notify_update_shadowed_delete; - if (kind == svn_node_dir) - node_kind = svn_node_dir; - else - node_kind = svn_node_file; - - do_notification(eb, local_abspath, node_kind, action, scratch_pool); + do_notification(eb, local_abspath, kind, action, scratch_pool); } svn_pool_destroy(scratch_pool); @@ -1991,6 +1850,7 @@ add_directory(const char *path, struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct dir_baton *db; + apr_pool_t *scratch_pool = svn_pool_create(pool); svn_node_kind_t kind; svn_wc__db_status_t status; svn_node_kind_t wc_kind; @@ -2008,6 +1868,9 @@ add_directory(const char *path, if (db->skip_this) return SVN_NO_ERROR; + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + NULL, eb, pb, db->pool, scratch_pool)); + SVN_ERR(mark_directory_edited(db, pool)); if (strcmp(eb->target_abspath, db->local_abspath) == 0) @@ -2037,13 +1900,26 @@ add_directory(const char *path, "administrative directory"), svn_dirent_local_style(db->local_abspath, pool)); - SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool)); + if (!eb->clean_checkout) + { + SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool)); + + err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, + eb->db, db->local_abspath, + scratch_pool, scratch_pool); + } + else + { + kind = svn_node_none; + status = svn_wc__db_status_not_present; + wc_kind = svn_node_unknown; + conflicted = FALSE; + err = NULL; + } - err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, - eb->db, db->local_abspath, db->pool, db->pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) @@ -2056,63 +1932,68 @@ add_directory(const char *path, versioned_locally_and_present = FALSE; } - else if (wc_kind == svn_node_dir - && status == svn_wc__db_status_normal) + else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown) { - /* !! We found the root of a separate working copy obstructing the wc !! - - If the directory would be part of our own working copy then - we wouldn't have been called as an add_directory(). - - The only thing we can do is add a not-present node, to allow - a future update to bring in the new files when the problem is - resolved. Note that svn_wc__db_base_add_not_present_node() - explicitly adds the node into the parent's node database. */ - - SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath, - db->new_relpath, - eb->repos_root, - eb->repos_uuid, - *eb->target_revision, - svn_node_file, - NULL, NULL, - pool)); - - SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); - db->skip_this = TRUE; - db->already_notified = TRUE; - - do_notification(eb, db->local_abspath, svn_node_dir, - svn_wc_notify_update_skip_obstruction, pool); - - return SVN_NO_ERROR; + SVN_ERR_ASSERT(conflicted); + versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ } else if (status == svn_wc__db_status_normal - && (wc_kind == svn_node_file - || wc_kind == svn_node_symlink)) + || status == svn_wc__db_status_incomplete) { - /* We found a file external occupating the place we need in BASE. + svn_boolean_t root; - We can't add a not-present node in this case as that would overwrite - the file external. Luckily the file external itself stops us from - forgetting a child of this parent directory like an obstructing - working copy would. + SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath, + scratch_pool)); - The reason we get here is that the adm crawler doesn't report - file externals. - */ + if (root) + { + /* !! We found the root of a working copy obstructing the wc !! - SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); + If the directory would be part of our own working copy then + we wouldn't have been called as an add_directory(). + + The only thing we can do is add a not-present node, to allow + a future update to bring in the new files when the problem is + resolved. Note that svn_wc__db_base_add_not_present_node() + explicitly adds the node into the parent's node database. */ + + svn_hash_sets(pb->not_present_nodes, + apr_pstrdup(pb->pool, db->name), + svn_node_kind_to_word(svn_node_dir)); + } + else if (wc_kind == svn_node_dir) + { + /* We have an editor violation. Github sometimes does this + in its subversion compatibility code, when changing the + depth of a working copy, or on updates from incomplete */ + } + else + { + /* We found a file external occupating the place we need in BASE. + + We can't add a not-present node in this case as that would overwrite + the file external. Luckily the file external itself stops us from + forgetting a child of this parent directory like an obstructing + working copy would. + + The reason we get here is that the adm crawler doesn't report + file externals. + */ + SVN_ERR_ASSERT(wc_kind == svn_node_file + || wc_kind == svn_node_symlink); + } + + SVN_ERR(remember_skipped_tree(eb, db->local_abspath, scratch_pool)); db->skip_this = TRUE; db->already_notified = TRUE; - do_notification(eb, db->local_abspath, svn_node_file, - svn_wc_notify_update_skip_obstruction, pool); + do_notification(eb, db->local_abspath, wc_kind, + svn_wc_notify_update_skip_obstruction, scratch_pool); + + svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } - else if (wc_kind == svn_node_unknown) - versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ else versioned_locally_and_present = IS_NODE_PRESENT(status); @@ -2134,7 +2015,7 @@ add_directory(const char *path, eb->db, db->local_abspath, tree_conflict, - db->pool, db->pool)); + db->pool, scratch_pool)); tree_conflict = svn_wc__conflict_skel_create(db->pool); @@ -2143,7 +2024,7 @@ add_directory(const char *path, eb->db, db->local_abspath, reason, svn_wc_conflict_action_replace, move_src_op_root_abspath, - db->pool, db->pool)); + db->pool, scratch_pool)); /* And now stop checking for conflicts here and just perform a shadowed update */ @@ -2154,7 +2035,8 @@ add_directory(const char *path, } else SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, - eb->db, db->local_abspath, pool)); + eb->db, db->local_abspath, + scratch_pool)); } /* Now the "usual" behaviour if already conflicted. Skip it. */ @@ -2177,18 +2059,13 @@ add_directory(const char *path, Note that we can safely assume that no present base node exists, because then we would not have received an add_directory. */ - SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath, - db->new_relpath, - eb->repos_root, - eb->repos_uuid, - *eb->target_revision, - svn_node_dir, - NULL, NULL, - pool)); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name), + svn_node_kind_to_word(svn_node_dir)); - /* ### TODO: Also print victim_path in the skip msg. */ do_notification(eb, db->local_abspath, svn_node_dir, - svn_wc_notify_skip_conflicted, pool); + svn_wc_notify_skip_conflicted, scratch_pool); + + svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } else if (conflict_ignored) @@ -2221,7 +2098,7 @@ add_directory(const char *path, SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, eb->db, db->local_abspath, - pool, pool)); + scratch_pool, scratch_pool)); /* Is there *something* that is not a dir? */ @@ -2244,7 +2121,7 @@ add_directory(const char *path, db->local_abspath, status, FALSE, svn_node_none, svn_wc_conflict_action_add, - pool, pool)); + db->pool, scratch_pool)); } if (tree_conflict == NULL) @@ -2272,7 +2149,7 @@ add_directory(const char *path, eb->db, db->local_abspath, svn_wc_conflict_reason_unversioned, svn_wc_conflict_action_add, NULL, - db->pool, pool)); + db->pool, scratch_pool)); db->edit_conflict = tree_conflict; } } @@ -2280,14 +2157,17 @@ add_directory(const char *path, if (tree_conflict) SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath, db->old_repos_relpath, db->old_revision, - db->new_relpath, - wc_kind, - svn_node_dir, - db->pool, pool)); + db->new_repos_relpath, + wc_kind, svn_node_dir, + pb->deletion_conflicts + ? svn_hash_gets(pb->deletion_conflicts, + db->name) + : NULL, + db->pool, scratch_pool)); SVN_ERR(svn_wc__db_base_add_incomplete_directory( eb->db, db->local_abspath, - db->new_relpath, + db->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, @@ -2296,28 +2176,20 @@ add_directory(const char *path, (! db->shadowed && status == svn_wc__db_status_added), tree_conflict, NULL, - pool)); + scratch_pool)); /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just updating the DB */ if (!db->shadowed) - SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool)); + SVN_ERR(svn_wc__ensure_directory(db->local_abspath, scratch_pool)); if (tree_conflict != NULL) { - if (eb->conflict_func) - SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, - tree_conflict, - NULL /* merge_options */, - eb->conflict_func, - eb->conflict_baton, - eb->cancel_func, - eb->cancel_baton, - pool)); + db->edit_conflict = tree_conflict; db->already_notified = TRUE; do_notification(eb, db->local_abspath, svn_node_dir, - svn_wc_notify_tree_conflict, pool); + svn_wc_notify_tree_conflict, scratch_pool); } @@ -2339,9 +2211,12 @@ add_directory(const char *path, db->already_notified = TRUE; - do_notification(eb, db->local_abspath, svn_node_dir, action, pool); + do_notification(eb, db->local_abspath, svn_node_dir, action, + scratch_pool); } + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; } @@ -2416,6 +2291,10 @@ open_directory(const char *path, db->was_incomplete = (base_status == svn_wc__db_status_incomplete); + SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, + db->old_repos_relpath, eb, pb, + db->pool, pool)); + /* Is this path a conflict victim? */ if (db->shadowed) conflicted = FALSE; /* Conflict applies to WORKING */ @@ -2475,7 +2354,7 @@ open_directory(const char *path, /* Mark directory as being at target_revision and URL, but incomplete. */ SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath, - db->new_relpath, + db->new_repos_relpath, *eb->target_revision, pool)); @@ -2498,7 +2377,7 @@ change_dir_prop(void *dir_baton, propchange = apr_array_push(db->propchanges); propchange->name = apr_pstrdup(db->pool, name); - propchange->value = value ? svn_string_dup(value, db->pool) : NULL; + propchange->value = svn_string_dup(value, db->pool); if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind) SVN_ERR(mark_directory_edited(db, pool)); @@ -2619,7 +2498,7 @@ close_directory(void *dir_baton, hi != NULL; hi = apr_hash_next(hi)) { - const char *propname = svn__apr_hash_index_key(hi); + const char *propname = apr_hash_this_key(hi); svn_prop_t *prop = apr_array_push(regular_prop_changes); /* Record a deletion for PROPNAME. */ @@ -2702,7 +2581,8 @@ close_directory(void *dir_baton, /* Check if we should add some not-present markers before marking the directory complete (Issue #3569) */ { - apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath); + apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, + db->new_repos_relpath); if (new_children != NULL) { @@ -2723,11 +2603,11 @@ close_directory(void *dir_baton, svn_pool_clear(iterpool); - child_name = svn__apr_hash_index_key(hi); + child_name = apr_hash_this_key(hi); child_abspath = svn_dirent_join(db->local_abspath, child_name, iterpool); - dirent = svn__apr_hash_index_val(hi); + dirent = apr_hash_this_val(hi); child_kind = (dirent->kind == svn_node_dir) ? svn_node_dir : svn_node_file; @@ -2758,7 +2638,7 @@ close_directory(void *dir_baton, svn_error_clear(err); - child_relpath = svn_relpath_join(db->new_relpath, child_name, + child_relpath = svn_relpath_join(db->new_repos_relpath, child_name, iterpool); SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, @@ -2776,7 +2656,7 @@ close_directory(void *dir_baton, } } - if (apr_hash_count(db->not_present_files)) + if (apr_hash_count(db->not_present_nodes)) { apr_hash_index_t *hi; apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -2786,17 +2666,18 @@ close_directory(void *dir_baton, transaction, but I can't even trigger it. I've tried ra_local, ra_svn, ra_neon, ra_serf and they all call close_file before close_dir. */ - for (hi = apr_hash_first(scratch_pool, db->not_present_files); + for (hi = apr_hash_first(scratch_pool, db->not_present_nodes); hi; hi = apr_hash_next(hi)) { - const char *child = svn__apr_hash_index_key(hi); + const char *child = apr_hash_this_key(hi); const char *child_abspath, *child_relpath; + svn_node_kind_t kind = svn_node_kind_from_word(apr_hash_this_val(hi)); svn_pool_clear(iterpool); child_abspath = svn_dirent_join(db->local_abspath, child, iterpool); - child_relpath = svn_dirent_join(db->new_relpath, child, iterpool); + child_relpath = svn_dirent_join(db->new_repos_relpath, child, iterpool); SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, child_abspath, @@ -2804,7 +2685,7 @@ close_directory(void *dir_baton, eb->repos_root, eb->repos_uuid, *eb->target_revision, - svn_node_file, + kind, NULL, NULL, iterpool)); } @@ -2872,8 +2753,14 @@ close_directory(void *dir_baton, db->local_abspath, db->old_repos_relpath, db->old_revision, - db->new_relpath, + db->new_repos_relpath, svn_node_dir, svn_node_dir, + (db->parent_baton + && db->parent_baton->deletion_conflicts) + ? svn_hash_gets( + db->parent_baton->deletion_conflicts, + db->name) + : NULL, db->pool, scratch_pool)); SVN_ERR(svn_wc__conflict_create_markers(&work_item, @@ -2904,7 +2791,7 @@ close_directory(void *dir_baton, SVN_ERR(svn_wc__db_base_add_directory( eb->db, db->local_abspath, eb->wcroot_abspath, - db->new_relpath, + db->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, props, @@ -2914,10 +2801,9 @@ close_directory(void *dir_baton, (dav_prop_changes->nelts > 0) ? svn_prop_array_to_hash(dav_prop_changes, pool) : NULL, - conflict_skel, (! db->shadowed) && new_base_props != NULL, - new_actual_props, - iprops, all_work_items, + new_actual_props, iprops, + conflict_skel, all_work_items, scratch_pool)); } @@ -2926,8 +2812,12 @@ close_directory(void *dir_baton, eb->cancel_func, eb->cancel_baton, scratch_pool)); + if (db->parent_baton) + svn_hash_sets(db->parent_baton->not_present_nodes, db->name, NULL); + if (conflict_skel && eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, + svn_node_dir, conflict_skel, NULL /* merge_options */, eb->conflict_func, @@ -2962,6 +2852,9 @@ close_directory(void *dir_baton, eb->notify_func(eb->notify_baton, notify, scratch_pool); } + if (db->edited) + eb->edited = db->edited; + /* We're done with this directory, so remove one reference from the bump information. */ SVN_ERR(maybe_release_dir_info(db)); @@ -2985,14 +2878,12 @@ absent_node(const char *path, svn_error_t *err; svn_wc__db_status_t status; svn_node_kind_t kind; + svn_skel_t *tree_conflict = NULL; if (pb->skip_this) return SVN_NO_ERROR; - SVN_ERR(mark_directory_edited(pb, scratch_pool)); - local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool); - /* If an item by this name is scheduled for addition that's a genuine tree-conflict. */ err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, @@ -3012,6 +2903,10 @@ absent_node(const char *path, kind = svn_node_unknown; } + if (status != svn_wc__db_status_server_excluded) + SVN_ERR(mark_directory_edited(pb, scratch_pool)); + /* Else fall through as we should update the revision anyway */ + if (status == svn_wc__db_status_normal) { svn_boolean_t wcroot; @@ -3035,31 +2930,53 @@ absent_node(const char *path, } else { - /* The server asks us to replace a file external - (Existing BASE node; not reported by the working copy crawler or - there would have been a delete_entry() call. - - There is no way we can store this state in the working copy as - the BASE layer is already filled. + svn_boolean_t file_external; + svn_revnum_t revnum; - We could error out, but that is not helping anybody; the user is not - even seeing with what the file external would be replaced, so let's - report a skip and continue the update. - */ + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &revnum, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + &file_external, + eb->db, local_abspath, + scratch_pool, scratch_pool)); - if (eb->notify_func) + if (file_external) { - svn_wc_notify_t *notify; - notify = svn_wc_create_notify( + /* The server asks us to replace a file external + (Existing BASE node; not reported by the working copy crawler + or there would have been a delete_entry() call. + + There is no way we can store this state in the working copy as + the BASE layer is already filled. + We could error out, but that is not helping anybody; the user is not + even seeing with what the file external would be replaced, so let's + report a skip and continue the update. + */ + + if (eb->notify_func) + { + svn_wc_notify_t *notify; + notify = svn_wc_create_notify( local_abspath, svn_wc_notify_update_skip_obstruction, scratch_pool); - eb->notify_func(eb->notify_baton, notify, scratch_pool); + eb->notify_func(eb->notify_baton, notify, scratch_pool); + } + + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; } + else + { + /* We have a normal local node that will now be hidden for the + user. Let's try to delete what is there. This may introduce + tree conflicts if there are local changes */ + SVN_ERR(delete_entry(path, revnum, pb, scratch_pool)); - svn_pool_destroy(scratch_pool); - return SVN_NO_ERROR; + /* delete_entry() promises that BASE is empty after the operation, + so we can just fall through now */ + } } } else if (status == svn_wc__db_status_not_present @@ -3073,24 +2990,26 @@ absent_node(const char *path, { /* We have a local addition. If this would be a BASE node it would have been deleted before we get here. (Which might have turned it into - a copy). - - ### This should be recorded as a tree conflict and the update - ### can just continue, as we can just record the absent status - ### in BASE. - */ + a copy). */ SVN_ERR_ASSERT(status != svn_wc__db_status_normal); - return svn_error_createf( - SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, - _("Failed to mark '%s' absent: item of the same name is already " - "scheduled for addition"), - svn_dirent_local_style(local_abspath, pool)); + if (!pb->shadowed && !pb->edit_obstructed) + SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, + status, FALSE, svn_node_unknown, + svn_wc_conflict_action_add, + scratch_pool, scratch_pool)); + } { const char *repos_relpath; - repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool); + repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, scratch_pool); + + if (tree_conflict) + SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, + NULL, SVN_INVALID_REVNUM, repos_relpath, + kind, svn_node_unknown, NULL, + scratch_pool, scratch_pool)); /* Insert an excluded node below the parent node to note that this child is absent. (This puts it in the parent db if the child is obstructed) */ @@ -3100,8 +3019,24 @@ absent_node(const char *path, *(eb->target_revision), absent_kind, svn_wc__db_status_server_excluded, - NULL, NULL, + tree_conflict, NULL, scratch_pool)); + + if (tree_conflict) + { + if (eb->conflict_func) + SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, + kind, + tree_conflict, + NULL /* merge_options */, + eb->conflict_func, + eb->conflict_baton, + eb->cancel_func, + eb->cancel_baton, + scratch_pool)); + do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict, + scratch_pool); + } } svn_pool_destroy(scratch_pool); @@ -3142,13 +3077,13 @@ add_file(const char *path, struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct file_baton *fb; - svn_node_kind_t kind = svn_node_none; - svn_node_kind_t wc_kind = svn_node_unknown; - svn_wc__db_status_t status = svn_wc__db_status_normal; + svn_node_kind_t kind; + svn_node_kind_t wc_kind; + svn_wc__db_status_t status; apr_pool_t *scratch_pool; - svn_boolean_t conflicted = FALSE; + svn_boolean_t conflicted; svn_boolean_t conflict_ignored = FALSE; - svn_boolean_t versioned_locally_and_present = FALSE; + svn_boolean_t versioned_locally_and_present; svn_skel_t *tree_conflict = NULL; svn_error_t *err = SVN_NO_ERROR; @@ -3160,6 +3095,8 @@ add_file(const char *path, if (fb->skip_this) return SVN_NO_ERROR; + SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath, + NULL, eb, pb, fb->pool, pool)); SVN_ERR(mark_file_edited(fb, pool)); /* The file_pool can stick around for a *long* time, so we want to @@ -3186,6 +3123,13 @@ add_file(const char *path, eb->db, fb->local_abspath, scratch_pool, scratch_pool); } + else + { + kind = svn_node_none; + status = svn_wc__db_status_not_present; + wc_kind = svn_node_unknown; + conflicted = FALSE; + } if (err) { @@ -3198,58 +3142,68 @@ add_file(const char *path, versioned_locally_and_present = FALSE; } - else if (wc_kind == svn_node_dir - && status == svn_wc__db_status_normal) + else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown) { - /* !! We found the root of a separate working copy obstructing the wc !! + SVN_ERR_ASSERT(conflicted); + versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ + } + else if (status == svn_wc__db_status_normal + || status == svn_wc__db_status_incomplete) + { + svn_boolean_t root; - If the directory would be part of our own working copy then - we wouldn't have been called as an add_file(). + SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath, + scratch_pool)); - The only thing we can do is add a not-present node, to allow - a future update to bring in the new files when the problem is - resolved. */ - svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), - (void *)1); + if (root) + { + /* !! We found the root of a working copy obstructing the wc !! - SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); - fb->skip_this = TRUE; - fb->already_notified = TRUE; + If the directory would be part of our own working copy then + we wouldn't have been called as an add_directory(). - do_notification(eb, fb->local_abspath, svn_node_file, - svn_wc_notify_update_skip_obstruction, scratch_pool); + The only thing we can do is add a not-present node, to allow + a future update to bring in the new files when the problem is + resolved. Note that svn_wc__db_base_add_not_present_node() + explicitly adds the node into the parent's node database. */ - svn_pool_destroy(scratch_pool); + svn_hash_sets(pb->not_present_nodes, + apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_dir)); + } + else if (wc_kind == svn_node_dir) + { + /* We have an editor violation. Github sometimes does this + in its subversion compatibility code, when changing the + depth of a working copy, or on updates from incomplete */ + } + else + { + /* We found a file external occupating the place we need in BASE. - return SVN_NO_ERROR; - } - else if (status == svn_wc__db_status_normal - && (wc_kind == svn_node_file - || wc_kind == svn_node_symlink)) - { - /* We found a file external occupating the place we need in BASE. + We can't add a not-present node in this case as that would overwrite + the file external. Luckily the file external itself stops us from + forgetting a child of this parent directory like an obstructing + working copy would. - We can't add a not-present node in this case as that would overwrite - the file external. Luckily the file external itself stops us from - forgetting a child of this parent directory like an obstructing - working copy would. + The reason we get here is that the adm crawler doesn't report + file externals. + */ + SVN_ERR_ASSERT(wc_kind == svn_node_file + || wc_kind == svn_node_symlink); + } - The reason we get here is that the adm crawler doesn't report - file externals. - */ SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); fb->skip_this = TRUE; fb->already_notified = TRUE; - do_notification(eb, fb->local_abspath, svn_node_file, + do_notification(eb, fb->local_abspath, wc_kind, svn_wc_notify_update_skip_obstruction, scratch_pool); svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } - else if (wc_kind == svn_node_unknown) - versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ else versioned_locally_and_present = IS_NODE_PRESENT(status); @@ -3274,7 +3228,7 @@ add_file(const char *path, eb->db, fb->local_abspath, tree_conflict, - fb->pool, fb->pool)); + fb->pool, scratch_pool)); tree_conflict = svn_wc__conflict_skel_create(fb->pool); @@ -3283,7 +3237,7 @@ add_file(const char *path, eb->db, fb->local_abspath, reason, svn_wc_conflict_action_replace, move_src_op_root_abspath, - fb->pool, fb->pool)); + fb->pool, scratch_pool)); /* And now stop checking for conflicts here and just perform a shadowed update */ @@ -3316,10 +3270,10 @@ add_file(const char *path, Note that we can safely assume that no present base node exists, because then we would not have received an add_file. */ - svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), - (void *)1); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_file)); - do_notification(eb, fb->local_abspath, svn_node_unknown, + do_notification(eb, fb->local_abspath, svn_node_file, svn_wc_notify_skip_conflicted, scratch_pool); svn_pool_destroy(scratch_pool); @@ -3380,7 +3334,7 @@ add_file(const char *path, fb->local_abspath, status, FALSE, svn_node_none, svn_wc_conflict_action_add, - scratch_pool, scratch_pool)); + fb->pool, scratch_pool)); } if (tree_conflict == NULL) @@ -3421,8 +3375,8 @@ add_file(const char *path, || *eb->target_basename == '\0' || (strcmp(fb->local_abspath, eb->target_abspath) != 0)) { - svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), - (void *)1); + svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name), + svn_node_kind_to_word(svn_node_file)); } if (tree_conflict != NULL) @@ -3432,9 +3386,12 @@ add_file(const char *path, fb->local_abspath, fb->old_repos_relpath, fb->old_revision, - fb->new_relpath, - wc_kind, - svn_node_file, + fb->new_repos_relpath, + wc_kind, svn_node_file, + pb->deletion_conflicts + ? svn_hash_gets(pb->deletion_conflicts, + fb->name) + : NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, @@ -3442,15 +3399,7 @@ add_file(const char *path, tree_conflict, NULL, scratch_pool)); - if (eb->conflict_func) - SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, - tree_conflict, - NULL /* merge_options */, - eb->conflict_func, - eb->conflict_baton, - eb->cancel_func, - eb->cancel_baton, - scratch_pool)); + fb->edit_conflict = tree_conflict; fb->already_notified = TRUE; do_notification(eb, fb->local_abspath, svn_node_file, @@ -3514,7 +3463,6 @@ open_file(const char *path, /* Sanity check. */ - /* If replacing, make sure the .svn entry already exists. */ SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision, &fb->old_repos_relpath, NULL, NULL, &fb->changed_rev, &fb->changed_date, @@ -3536,6 +3484,10 @@ open_file(const char *path, eb->db, fb->local_abspath, fb->pool, scratch_pool)); + SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath, + fb->old_repos_relpath, eb, pb, + fb->pool, scratch_pool)); + /* Is this path a conflict victim? */ if (fb->shadowed) conflicted = FALSE; /* Conflict applies to WORKING */ @@ -3615,12 +3567,6 @@ lazy_open_source(svn_stream_t **stream, return SVN_NO_ERROR; } -struct lazy_target_baton { - struct file_baton *fb; - struct handler_baton *hb; - struct edit_baton *eb; -}; - /* Implements svn_stream_lazyopen_func_t. */ static svn_error_t * lazy_open_target(svn_stream_t **stream, @@ -3628,13 +3574,23 @@ lazy_open_target(svn_stream_t **stream, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - struct lazy_target_baton *tb = baton; - - SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath, - NULL, &tb->hb->new_text_base_sha1_checksum, - tb->fb->edit_baton->db, - tb->eb->wcroot_abspath, - result_pool, scratch_pool)); + struct handler_baton *hb = baton; + svn_wc__db_install_data_t *install_data; + + /* By convention return value is undefined on error, but we rely + on HB->INSTALL_DATA value in window_handler() and abort + INSTALL_STREAM if is not NULL on error. + So we store INSTALL_DATA to local variable first, to leave + HB->INSTALL_DATA unchanged on error. */ + SVN_ERR(svn_wc__db_pristine_prepare_install(stream, + &install_data, + &hb->new_text_base_sha1_checksum, + NULL, + hb->fb->edit_baton->db, + hb->fb->dir_baton->local_abspath, + result_pool, scratch_pool)); + + hb->install_data = install_data; return SVN_NO_ERROR; } @@ -3654,7 +3610,6 @@ apply_textdelta(void *file_baton, const svn_checksum_t *recorded_base_checksum; svn_checksum_t *expected_base_checksum; svn_stream_t *source; - struct lazy_target_baton *tb; svn_stream_t *target; if (fb->skip_this) @@ -3748,16 +3703,12 @@ apply_textdelta(void *file_baton, hb->source_checksum_stream = source; } - tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton)); - tb->hb = hb; - tb->fb = fb; - tb->eb = eb; - target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool); + target = svn_stream_lazyopen_create(lazy_open_target, hb, TRUE, handler_pool); /* Prepare to apply the delta. */ svn_txdelta_apply(source, target, hb->new_text_base_md5_digest, - hb->new_text_base_tmp_abspath /* error_info */, + fb->local_abspath /* error_info */, handler_pool, &hb->apply_handler, &hb->apply_baton); @@ -3788,7 +3739,7 @@ change_file_prop(void *file_baton, /* Push a new propchange to the file baton's array of propchanges */ propchange = apr_array_push(fb->propchanges); propchange->name = apr_pstrdup(fb->pool, name); - propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; + propchange->value = svn_string_dup(value, fb->pool); if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind) SVN_ERR(mark_file_edited(fb, scratch_pool)); @@ -3851,9 +3802,9 @@ change_file_prop(void *file_baton, SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, fb->local_abspath, fb->old_repos_relpath, - fb->old_revision, fb->new_relpath, + fb->old_revision, fb->new_repos_relpath, svn_node_file, svn_node_file, - fb->pool, scratch_pool)); + NULL, fb->pool, scratch_pool)); /* Create a copy of the existing (pre update) BASE node in WORKING, mark a tree conflict and handle the rest of the update as @@ -3913,13 +3864,13 @@ svn_wc__perform_file_merge(svn_skel_t **work_items, const char *merge_left; svn_boolean_t delete_left = FALSE; const char *path_ext = ""; - const char *new_text_base_tmp_abspath; + const char *new_pristine_abspath; enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged; svn_skel_t *work_item; *work_items = NULL; - SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath, + SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, db, wri_abspath, new_checksum, scratch_pool, scratch_pool)); @@ -3972,7 +3923,7 @@ svn_wc__perform_file_merge(svn_skel_t **work_items, &merge_outcome, db, merge_left, - new_text_base_tmp_abspath, + new_pristine_abspath, local_abspath, wri_abspath, oldrev_str, newrev_str, mine_str, @@ -4321,11 +4272,11 @@ close_file(void *file_baton, { /* If we lose the lock, but not because we are switching to another url, remove the state lock from the wc */ - if (! eb->switch_relpath - || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0) + if (! eb->switch_repos_relpath + || strcmp(fb->new_repos_relpath, fb->old_repos_relpath) == 0) { SVN_ERR_ASSERT(prop->value == NULL); - SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, + SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, NULL, scratch_pool)); lock_state = svn_wc_notify_lock_state_unlocked; @@ -4564,8 +4515,13 @@ close_file(void *file_baton, fb->local_abspath, fb->old_repos_relpath, fb->old_revision, - fb->new_relpath, + fb->new_repos_relpath, svn_node_file, svn_node_file, + fb->dir_baton->deletion_conflicts + ? svn_hash_gets( + fb->dir_baton->deletion_conflicts, + fb->name) + : NULL, fb->pool, scratch_pool)); SVN_ERR(svn_wc__conflict_create_markers(&work_item, @@ -4593,7 +4549,7 @@ close_file(void *file_baton, SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath, eb->wcroot_abspath, - fb->new_relpath, + fb->new_repos_relpath, eb->repos_root, eb->repos_uuid, *eb->target_revision, new_base_props, @@ -4618,6 +4574,7 @@ close_file(void *file_baton, if (conflict_skel && eb->conflict_func) SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, + svn_node_file, conflict_skel, NULL /* merge_options */, eb->conflict_func, @@ -4628,7 +4585,7 @@ close_file(void *file_baton, /* Deal with the WORKING tree, based on updates to the BASE tree. */ - svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL); + svn_hash_sets(fb->dir_baton->not_present_nodes, fb->name, NULL); /* Send a notification to the callback function. (Skip notifications about files which were already notified for another reason.) */ @@ -4687,6 +4644,78 @@ close_file(void *file_baton, } +/* Implements svn_wc__proplist_receiver_t. + * Check for the presence of an svn:keywords property and queues an install_file + * work queue item if present. Thus, when the work queue is run to complete the + * switch operation, all files with keywords will go through the translation + * process so URLs etc are updated. */ +static svn_error_t * +update_keywords_after_switch_cb(void *baton, + const char *local_abspath, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + svn_string_t *propval; + svn_boolean_t modified; + svn_boolean_t record_fileinfo; + svn_skel_t *work_items; + const char *install_from; + + propval = svn_hash_gets(props, SVN_PROP_KEYWORDS); + if (!propval) + return SVN_NO_ERROR; + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db, + local_abspath, FALSE, + scratch_pool)); + if (modified) + { + const char *temp_dir_abspath; + svn_stream_t *working_stream; + svn_stream_t *install_from_stream; + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, eb->db, + local_abspath, scratch_pool, + scratch_pool)); + SVN_ERR(svn_stream_open_readonly(&working_stream, local_abspath, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_open_unique(&install_from_stream, &install_from, + temp_dir_abspath, svn_io_file_del_none, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_copy3(working_stream, install_from_stream, + eb->cancel_func, eb->cancel_baton, + scratch_pool)); + record_fileinfo = FALSE; + } + else + { + install_from = NULL; + record_fileinfo = TRUE; + } + + SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath, + install_from, + eb->use_commit_times, + record_fileinfo, + scratch_pool, scratch_pool)); + if (install_from) + { + svn_skel_t *work_item; + + SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db, + local_abspath, install_from, + scratch_pool, scratch_pool)); + work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); + } + + SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items, + scratch_pool)); + + return SVN_NO_ERROR; +} + + /* An svn_delta_editor_t function. */ static svn_error_t * close_edit(void *edit_baton, @@ -4728,12 +4757,13 @@ close_edit(void *edit_baton, SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db, eb->target_abspath, eb->requested_depth, - eb->switch_relpath, + eb->switch_repos_relpath, eb->repos_root, eb->repos_uuid, *(eb->target_revision), eb->skipped_trees, eb->wcroot_iprops, + ! eb->edited, eb->notify_func, eb->notify_baton, eb->pool)); @@ -4773,15 +4803,36 @@ close_edit(void *edit_baton, If so, we should get rid of this excluded node now. */ SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath, - FALSE /* keep_as_working */, - FALSE /* queue_deletes */, - FALSE /* remove_locks */, + TRUE, FALSE, FALSE, SVN_INVALID_REVNUM, NULL, NULL, scratch_pool)); } } } + /* Update keywords in switched files. + GOTO #1975 (the year of the Altair 8800). */ + if (eb->switch_repos_relpath) + { + svn_depth_t depth; + + if (eb->requested_depth > svn_depth_empty) + depth = eb->requested_depth; + else + depth = svn_depth_infinity; + + SVN_ERR(svn_wc__db_read_props_streamily(eb->db, + eb->target_abspath, + depth, + FALSE, /* pristine */ + NULL, /* changelists */ + update_keywords_after_switch_cb, + eb, + eb->cancel_func, + eb->cancel_baton, + scratch_pool)); + } + /* The edit is over: run the wq with proper cancel support, but first kill the handler that would run it on the pool cleanup at the end of this function. */ @@ -4854,9 +4905,11 @@ make_editor(svn_revnum_t *target_revision, /* Get the anchor's repository root and uuid. The anchor must already exist in BASE. */ - SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid, - db, anchor_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, &repos_root, + &repos_uuid, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + db, anchor_abspath, + result_pool, scratch_pool)); /* With WC-NG we need a valid repository root */ SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL); @@ -4884,10 +4937,10 @@ make_editor(svn_revnum_t *target_revision, edit_pool, scratch_pool)); if (switch_url) - eb->switch_relpath = + eb->switch_repos_relpath = svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool); else - eb->switch_relpath = NULL; + eb->switch_repos_relpath = NULL; if (svn_path_is_empty(target_basename)) eb->target_abspath = eb->anchor_abspath; @@ -4968,8 +5021,8 @@ make_editor(svn_revnum_t *target_revision, apr_hash_t *dirents; /* If we switch, we should look at the new relpath */ - if (eb->switch_relpath) - dir_repos_relpath = eb->switch_relpath; + if (eb->switch_repos_relpath) + dir_repos_relpath = eb->switch_repos_relpath; SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, repos_root, dir_repos_relpath, @@ -5022,9 +5075,9 @@ make_editor(svn_revnum_t *target_revision, apr_hash_t *dirents; /* If we switch, we should look at the new relpath */ - if (eb->switch_relpath) + if (eb->switch_repos_relpath) dir_repos_relpath = svn_relpath_join( - eb->switch_relpath, + eb->switch_repos_relpath, child_name, iterpool); SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, @@ -5223,6 +5276,8 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, svn_revnum_t changed_rev; apr_time_t changed_date; const char *changed_author; + svn_stream_t *tmp_base_contents; + svn_wc__db_install_data_t *install_data; svn_error_t *err; apr_pool_t *pool = scratch_pool; @@ -5344,18 +5399,35 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */ - { - svn_stream_t *tmp_base_contents; + if (copyfrom_url) + { + SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents, + &install_data, + &new_text_base_sha1_checksum, + &new_text_base_md5_checksum, + wc_ctx->db, local_abspath, + scratch_pool, scratch_pool)); + } + else + { + const char *tmp_dir_abspath; - SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents, - &tmp_text_base_abspath, - &new_text_base_md5_checksum, - &new_text_base_sha1_checksum, - wc_ctx->db, local_abspath, - pool, pool)); - SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents, - cancel_func, cancel_baton, pool)); - } + /* We are not installing a PRISTINE file, but we use the same code to + create whatever we want to install */ + + SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath, + db, dir_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath, + tmp_dir_abspath, svn_io_file_del_none, + scratch_pool, scratch_pool)); + + new_text_base_sha1_checksum = NULL; + new_text_base_md5_checksum = NULL; + } + SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents, + cancel_func, cancel_baton, pool)); /* If the caller gave us a new working file, copy it to a safe (temporary) location and set SOURCE_ABSPATH to that path. We'll then translate/copy @@ -5378,7 +5450,7 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, text base. */ if (copyfrom_url != NULL) { - SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath, + SVN_ERR(svn_wc__db_pristine_install(install_data, new_text_base_sha1_checksum, new_text_base_md5_checksum, pool)); } @@ -5445,9 +5517,6 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, } } - /* ### ideally, we would have a single DB operation, and queue the work - ### items on that. for now, we'll queue them with the second call. */ - SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath, new_base_props, changed_rev, diff --git a/subversion/libsvn_wc/upgrade.c b/subversion/libsvn_wc/upgrade.c index af615fd..aebf4eb 100644 --- a/subversion/libsvn_wc/upgrade.c +++ b/subversion/libsvn_wc/upgrade.c @@ -37,6 +37,7 @@ #include "tree_conflicts.h" #include "wc-queries.h" /* for STMT_* */ #include "workqueue.h" +#include "token-map.h" #include "svn_private_config.h" #include "private/svn_wc_private.h" @@ -193,7 +194,7 @@ read_many_wcprops(apr_hash_t **all_wcprops, hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); + const char *name = apr_hash_this_key(hi); svn_pool_clear(iterpool); @@ -293,15 +294,15 @@ get_versioned_subdirs(apr_array_header_t **children, hi; hi = apr_hash_next(hi)) { - const char *name = svn__apr_hash_index_key(hi); - const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi); + const char *name = apr_hash_this_key(hi); + const svn_wc_entry_t *entry = apr_hash_this_val(hi); const char *child_abspath; svn_boolean_t hidden; /* skip "this dir" */ if (*name == '\0') { - this_dir = svn__apr_hash_index_val(hi); + this_dir = apr_hash_this_val(hi); continue; } else if (entry->kind != svn_node_dir) @@ -400,7 +401,7 @@ build_lockfile_path(const char *local_dir_abspath, local_dir_abspath, svn_wc_get_adm_dir(result_pool), ADM_LOCK, - NULL); + SVN_VA_NULL); } @@ -612,13 +613,13 @@ ensure_repos_info(svn_wc_entry_t *entry, for (hi = apr_hash_first(scratch_pool, repos_cache); hi; hi = apr_hash_next(hi)) { - if (svn_uri__is_ancestor(svn__apr_hash_index_key(hi), entry->url)) + if (svn_uri__is_ancestor(apr_hash_this_key(hi), entry->url)) { if (!entry->repos) - entry->repos = svn__apr_hash_index_key(hi); + entry->repos = apr_hash_this_key(hi); if (!entry->uuid) - entry->uuid = svn__apr_hash_index_val(hi); + entry->uuid = apr_hash_this_val(hi); return SVN_NO_ERROR; } @@ -727,8 +728,7 @@ migrate_single_tree_conflict_data(svn_sqlite__db_t *sdb, hi; hi = apr_hash_next(hi)) { - const svn_wc_conflict_description2_t *conflict = - svn__apr_hash_index_val(hi); + const svn_wc_conflict_description2_t *conflict = apr_hash_this_val(hi); const char *conflict_relpath; const char *conflict_data; svn_sqlite__stmt_t *stmt; @@ -755,13 +755,13 @@ migrate_single_tree_conflict_data(svn_sqlite__db_t *sdb, { /* There is an existing ACTUAL row, so just update it. */ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_UPDATE_ACTUAL_CONFLICT_DATA)); + STMT_UPDATE_ACTUAL_CONFLICT)); } else { /* We need to insert an ACTUAL row with the tree conflict data. */ SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_INSERT_ACTUAL_CONFLICT_DATA)); + STMT_INSERT_ACTUAL_CONFLICT)); } SVN_ERR(svn_sqlite__bindf(stmt, "iss", wc_id, conflict_relpath, @@ -825,6 +825,190 @@ migrate_tree_conflict_data(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) return SVN_NO_ERROR; } +/* ### need much more docco + + ### this function should be called within a sqlite transaction. it makes + ### assumptions around this fact. + + Apply the various sets of properties to the database nodes based on + their existence/presence, the current state of the node, and the original + format of the working copy which provided these property sets. +*/ +static svn_error_t * +upgrade_apply_props(svn_sqlite__db_t *sdb, + const char *dir_abspath, + const char *local_relpath, + apr_hash_t *base_props, + apr_hash_t *revert_props, + apr_hash_t *working_props, + int original_format, + apr_int64_t wc_id, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + int top_op_depth = -1; + int below_op_depth = -1; + svn_wc__db_status_t top_presence; + svn_wc__db_status_t below_presence; + int affected_rows; + + /* ### working_props: use set_props_txn. + ### if working_props == NULL, then skip. what if they equal the + ### pristine props? we should probably do the compare here. + ### + ### base props go into WORKING_NODE if avail, otherwise BASE. + ### + ### revert only goes into BASE. (and WORKING better be there!) + + Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a + file was deleted, then a copy (potentially with props) was disallowed + and could not replace the deletion. An addition *could* be performed, + but that would never bring its own props. + + 1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a + bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT + construct a REVERT_PROPS if the target had no props. Thus, reverting + the delete/copy would see no REVERT_PROPS to restore, leaving the + props from the copy source intact, and appearing as if they are (now) + the base props for the previously-deleted file. (wc corruption) + + 1.4.6 ensured that an empty REVERT_PROPS would be established at all + times. See issue 2530, and r861670 as starting points. + + We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine + the handling of our inputs, relative to the state of this node. + */ + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + { + top_op_depth = svn_sqlite__column_int(stmt, 0); + top_presence = svn_sqlite__column_token(stmt, 3, presence_map); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + { + below_presence = svn_sqlite__column_token(stmt, 3, presence_map); + + /* There might be an intermediate layer on mixed-revision copies, + or when BASE is shadowed */ + if (below_presence == svn_wc__db_status_not_present + || below_presence == svn_wc__db_status_deleted) + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + { + below_presence = svn_sqlite__column_token(stmt, 3, presence_map); + below_op_depth = svn_sqlite__column_int(stmt, 0); + } + } + } + SVN_ERR(svn_sqlite__reset(stmt)); + + /* Detect the buggy scenario described above. We cannot upgrade this + working copy if we have no idea where BASE_PROPS should go. */ + if (original_format > SVN_WC__NO_REVERT_FILES + && revert_props == NULL + && top_op_depth != -1 + && top_presence == svn_wc__db_status_normal + && below_op_depth != -1 + && below_presence != svn_wc__db_status_not_present) + { + /* There should be REVERT_PROPS, so it appears that we just ran into + the described bug. Sigh. */ + return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, + _("The properties of '%s' are in an " + "indeterminate state and cannot be " + "upgraded. See issue #2530."), + svn_dirent_local_style( + svn_dirent_join(dir_abspath, local_relpath, + scratch_pool), scratch_pool)); + } + + /* Need at least one row, or two rows if there are revert props */ + if (top_op_depth == -1 + || (below_op_depth == -1 && revert_props)) + return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, + _("Insufficient NODES rows for '%s'"), + svn_dirent_local_style( + svn_dirent_join(dir_abspath, local_relpath, + scratch_pool), scratch_pool)); + + /* one row, base props only: upper row gets base props + two rows, base props only: lower row gets base props + two rows, revert props only: lower row gets revert props + two rows, base and revert props: upper row gets base, lower gets revert */ + + + if (revert_props || below_op_depth == -1) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + STMT_UPDATE_NODE_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", + wc_id, local_relpath, top_op_depth)); + SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + + SVN_ERR_ASSERT(affected_rows == 1); + } + + if (below_op_depth != -1) + { + apr_hash_t *props = revert_props ? revert_props : base_props; + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + STMT_UPDATE_NODE_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", + wc_id, local_relpath, below_op_depth)); + SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + + SVN_ERR_ASSERT(affected_rows == 1); + } + + /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */ + if (working_props != NULL + && base_props != NULL) + { + apr_array_header_t *diffs; + + SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool)); + + if (diffs->nelts == 0) + working_props = NULL; /* No differences */ + } + + if (working_props != NULL) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + STMT_UPDATE_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + SVN_ERR(svn_sqlite__bind_properties(stmt, 3, working_props, + scratch_pool)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + + if (affected_rows == 0) + { + /* We have to insert a row in ACTUAL */ + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + STMT_INSERT_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + if (*local_relpath != '\0') + SVN_ERR(svn_sqlite__bind_text(stmt, 3, + svn_relpath_dirname(local_relpath, + scratch_pool))); + SVN_ERR(svn_sqlite__bind_properties(stmt, 4, working_props, + scratch_pool)); + return svn_error_trace(svn_sqlite__step_done(stmt)); + } + } + + return SVN_NO_ERROR; +} + struct bump_baton { const char *wcroot_abspath; @@ -875,21 +1059,21 @@ migrate_node_props(const char *dir_abspath, apr_pstrcat(scratch_pool, name, SVN_WC__BASE_EXT, - (char *)NULL), + SVN_VA_NULL), scratch_pool); revert_abspath = svn_dirent_join(basedir_abspath, apr_pstrcat(scratch_pool, name, SVN_WC__REVERT_EXT, - (char *)NULL), + SVN_VA_NULL), scratch_pool); working_abspath = svn_dirent_join(propsdir_abspath, apr_pstrcat(scratch_pool, name, SVN_WC__WORK_EXT, - (char *)NULL), + SVN_VA_NULL), scratch_pool); } @@ -900,7 +1084,7 @@ migrate_node_props(const char *dir_abspath, SVN_ERR(read_propfile(&working_props, working_abspath, scratch_pool, scratch_pool)); - return svn_error_trace(svn_wc__db_upgrade_apply_props( + return svn_error_trace(upgrade_apply_props( sdb, new_wcroot_abspath, svn_relpath_join(dir_relpath, name, scratch_pool), base_props, revert_props, working_props, @@ -1017,7 +1201,7 @@ migrate_text_bases(apr_hash_t **text_bases_info, for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) { - const char *text_base_basename = svn__apr_hash_index_key(hi); + const char *text_base_basename = apr_hash_this_key(hi); svn_checksum_t *md5_checksum; svn_checksum_t *sha1_checksum; @@ -1248,7 +1432,7 @@ rename_pristine_file(void *baton, == PRISTINE_BASENAME_OLD_LEN)) { const char *new_abspath - = apr_pstrcat(pool, abspath, PRISTINE_STORAGE_EXT, (char *)NULL); + = apr_pstrcat(pool, abspath, PRISTINE_STORAGE_EXT, SVN_VA_NULL); SVN_ERR(svn_io_file_rename(abspath, new_abspath, pool)); } @@ -1349,7 +1533,8 @@ bump_to_29(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) /* Rename all pristine files, adding a ".svn-base" suffix. */ pristine_dir_abspath = svn_dirent_join_many(scratch_pool, wcroot_abspath, svn_wc_get_adm_dir(scratch_pool), - PRISTINE_STORAGE_RELPATH, NULL); + PRISTINE_STORAGE_RELPATH, + SVN_VA_NULL); SVN_ERR(svn_io_dir_walk2(pristine_dir_abspath, APR_FINFO_MIN, rename_pristine_file, NULL, scratch_pool)); @@ -1671,6 +1856,43 @@ bump_to_31(void *baton, 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, + apr_hash_t *cache_values, + apr_pool_t *scratch_pool) +{ + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_index_t *hi; + svn_sqlite__stmt_t *stmt; + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + STMT_UPDATE_BASE_NODE_DAV_CACHE)); + + /* Iterate over all the wcprops, writing each one to the wc_db. */ + for (hi = apr_hash_first(scratch_pool, cache_values); + hi; + hi = apr_hash_next(hi)) + { + const char *name = apr_hash_this_key(hi); + apr_hash_t *props = apr_hash_this_val(hi); + const char *local_relpath; + + svn_pool_clear(iterpool); + + local_relpath = svn_relpath_join(dir_relpath, name, iterpool); + + SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + struct upgrade_data_t { svn_sqlite__db_t *sdb; @@ -1808,8 +2030,8 @@ upgrade_to_wcng(void **dir_baton, SVN_ERR(read_wcprops(&all_wcprops, dir_abspath, scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__db_upgrade_apply_dav_cache(data->sdb, dir_relpath, - all_wcprops, scratch_pool)); + SVN_ERR(upgrade_apply_dav_cache(data->sdb, dir_relpath, wc_id, + all_wcprops, scratch_pool)); } /* Upgrade all the properties (including "this dir"). @@ -2144,40 +2366,6 @@ is_old_wcroot(const char *local_abspath, svn_dirent_local_style(parent_abspath, scratch_pool)); } -/* Data for upgrade_working_copy_txn(). */ -typedef struct upgrade_working_copy_baton_t -{ - svn_wc__db_t *db; - const char *dir_abspath; - svn_wc_upgrade_get_repos_info_t repos_info_func; - void *repos_info_baton; - apr_hash_t *repos_cache; - const struct upgrade_data_t *data; - svn_cancel_func_t cancel_func; - void *cancel_baton; - svn_wc_notify_func2_t notify_func; - void *notify_baton; - apr_pool_t *result_pool; -} upgrade_working_copy_baton_t; - - -/* Helper for svn_wc_upgrade. Implements svn_sqlite__transaction_callback_t */ -static svn_error_t * -upgrade_working_copy_txn(void *baton, - svn_sqlite__db_t *sdb, - apr_pool_t *scratch_pool) -{ - upgrade_working_copy_baton_t *b = baton; - - /* Upgrade the pre-wcng into a wcng in a temporary location. */ - return(upgrade_working_copy(NULL, b->db, b->dir_abspath, - b->repos_info_func, b->repos_info_baton, - b->repos_cache, b->data, - b->cancel_func, b->cancel_baton, - b->notify_func, b->notify_baton, - b->result_pool, scratch_pool)); -} - svn_error_t * svn_wc_upgrade(svn_wc_context_t *wc_ctx, const char *local_abspath, @@ -2197,7 +2385,6 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx, svn_wc_entry_t *this_dir; apr_hash_t *entries; const char *root_adm_abspath; - upgrade_working_copy_baton_t cb_baton; svn_error_t *err; int result_format; svn_boolean_t bumped_format; @@ -2295,22 +2482,14 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx, SVN_ERR(svn_wc__db_wclock_obtain(db, data.root_abspath, 0, FALSE, scratch_pool)); - cb_baton.db = db; - cb_baton.dir_abspath = local_abspath; - cb_baton.repos_info_func = repos_info_func; - cb_baton.repos_info_baton = repos_info_baton; - cb_baton.repos_cache = repos_cache; - cb_baton.data = &data; - cb_baton.cancel_func = cancel_func; - cb_baton.cancel_baton = cancel_baton; - cb_baton.notify_func = notify_func; - cb_baton.notify_baton = notify_baton; - cb_baton.result_pool = scratch_pool; - - SVN_ERR(svn_sqlite__with_lock(data.sdb, - upgrade_working_copy_txn, - &cb_baton, - scratch_pool)); + SVN_SQLITE__WITH_LOCK( + upgrade_working_copy(NULL, db, local_abspath, + repos_info_func, repos_info_baton, + repos_cache, &data, + cancel_func, cancel_baton, + notify_func, notify_baton, + scratch_pool, scratch_pool), + data.sdb); /* A workqueue item to move the pristine dir into place */ pristine_from = svn_wc__adm_child(data.root_abspath, PRISTINE_STORAGE_RELPATH, diff --git a/subversion/libsvn_wc/util.c b/subversion/libsvn_wc/util.c index a527eda..7bb2179 100644 --- a/subversion/libsvn_wc/util.c +++ b/subversion/libsvn_wc/util.c @@ -248,9 +248,34 @@ svn_wc_conflict_description_create_tree2( return conflict; } +svn_wc_conflict_version_t * +svn_wc_conflict_version_create2(const char *repos_url, + const char *repos_uuid, + const char *repos_relpath, + svn_revnum_t revision, + svn_node_kind_t kind, + apr_pool_t *result_pool) +{ + svn_wc_conflict_version_t *version; + + version = apr_pcalloc(result_pool, sizeof(*version)); + + SVN_ERR_ASSERT_NO_RETURN(svn_uri_is_canonical(repos_url, result_pool) + && svn_relpath_is_canonical(repos_relpath) + && SVN_IS_VALID_REVNUM(revision) + /* ### repos_uuid can be NULL :( */); + + version->repos_url = repos_url; + version->peg_rev = revision; + version->path_in_repos = repos_relpath; + version->node_kind = kind; + version->repos_uuid = repos_uuid; + + return version; +} svn_wc_conflict_description2_t * -svn_wc__conflict_description2_dup(const svn_wc_conflict_description2_t *conflict, +svn_wc_conflict_description2_dup(const svn_wc_conflict_description2_t *conflict, apr_pool_t *pool) { svn_wc_conflict_description2_t *new_conflict; @@ -281,36 +306,27 @@ svn_wc__conflict_description2_dup(const svn_wc_conflict_description2_t *conflict new_conflict->src_right_version = svn_wc_conflict_version_dup(conflict->src_right_version, pool); - return new_conflict; -} + /* ### For property conflicts, cd2 stores prop_reject_abspath in + * ### their_abspath, and stores theirs_abspath in merged_file. */ + if (conflict->prop_reject_abspath) + new_conflict->prop_reject_abspath = new_conflict->their_abspath; + + if (conflict->prop_value_base) + new_conflict->prop_value_base = + svn_string_dup(conflict->prop_value_base, pool); + if (conflict->prop_value_working) + new_conflict->prop_value_working = + svn_string_dup(conflict->prop_value_working, pool); + if (conflict->prop_value_incoming_old) + new_conflict->prop_value_incoming_old = + svn_string_dup(conflict->prop_value_incoming_old, pool); + if (conflict->prop_value_incoming_new) + new_conflict->prop_value_incoming_new = + svn_string_dup(conflict->prop_value_incoming_new, pool); -svn_wc_conflict_version_t * -svn_wc_conflict_version_create2(const char *repos_url, - const char *repos_uuid, - const char *repos_relpath, - svn_revnum_t revision, - svn_node_kind_t kind, - apr_pool_t *result_pool) -{ - svn_wc_conflict_version_t *version; - - version = apr_pcalloc(result_pool, sizeof(*version)); - - SVN_ERR_ASSERT_NO_RETURN(svn_uri_is_canonical(repos_url, result_pool) - && svn_relpath_is_canonical(repos_relpath) - && SVN_IS_VALID_REVNUM(revision) - /* ### repos_uuid can be NULL :( */); - - version->repos_url = repos_url; - version->peg_rev = revision; - version->path_in_repos = repos_relpath; - version->node_kind = kind; - version->repos_uuid = repos_uuid; - - return version; + return new_conflict; } - svn_wc_conflict_version_t * svn_wc_conflict_version_dup(const svn_wc_conflict_version_t *version, apr_pool_t *result_pool) @@ -339,7 +355,6 @@ svn_wc_conflict_version_dup(const svn_wc_conflict_version_t *version, return new_version; } - svn_wc_conflict_description_t * svn_wc__cd2_to_cd(const svn_wc_conflict_description2_t *conflict, apr_pool_t *result_pool) @@ -403,145 +418,6 @@ svn_wc__cd2_to_cd(const svn_wc_conflict_description2_t *conflict, svn_error_t * -svn_wc__status2_from_3(svn_wc_status2_t **status, - const svn_wc_status3_t *old_status, - svn_wc_context_t *wc_ctx, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const svn_wc_entry_t *entry = NULL; - - if (old_status == NULL) - { - *status = NULL; - return SVN_NO_ERROR; - } - - *status = apr_pcalloc(result_pool, sizeof(**status)); - - if (old_status->versioned) - { - svn_error_t *err; - err= svn_wc__get_entry(&entry, wc_ctx->db, local_abspath, FALSE, - svn_node_unknown, result_pool, scratch_pool); - - if (err && err->apr_err == SVN_ERR_NODE_UNEXPECTED_KIND) - svn_error_clear(err); - else - SVN_ERR(err); - } - - (*status)->entry = entry; - (*status)->copied = old_status->copied; - (*status)->repos_lock = svn_lock_dup(old_status->repos_lock, result_pool); - - if (old_status->repos_relpath) - (*status)->url = svn_path_url_add_component2(old_status->repos_root_url, - old_status->repos_relpath, - result_pool); - (*status)->ood_last_cmt_rev = old_status->ood_changed_rev; - (*status)->ood_last_cmt_date = old_status->ood_changed_date; - (*status)->ood_kind = old_status->ood_kind; - (*status)->ood_last_cmt_author = old_status->ood_changed_author; - - if (old_status->conflicted) - { - const svn_wc_conflict_description2_t *tree_conflict; - SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict, wc_ctx, local_abspath, - scratch_pool, scratch_pool)); - (*status)->tree_conflict = svn_wc__cd2_to_cd(tree_conflict, result_pool); - } - - (*status)->switched = old_status->switched; - - (*status)->text_status = old_status->node_status; - (*status)->prop_status = old_status->prop_status; - - (*status)->repos_text_status = old_status->repos_node_status; - (*status)->repos_prop_status = old_status->repos_prop_status; - - /* Some values might be inherited from properties */ - if (old_status->node_status == svn_wc_status_modified - || old_status->node_status == svn_wc_status_conflicted) - (*status)->text_status = old_status->text_status; - - /* (Currently a no-op, but just make sure it is ok) */ - if (old_status->repos_node_status == svn_wc_status_modified - || old_status->repos_node_status == svn_wc_status_conflicted) - (*status)->repos_text_status = old_status->repos_text_status; - - if (old_status->node_status == svn_wc_status_added) - (*status)->prop_status = svn_wc_status_none; /* No separate info */ - - /* Find pristine_text_status value */ - switch (old_status->text_status) - { - case svn_wc_status_none: - case svn_wc_status_normal: - case svn_wc_status_modified: - (*status)->pristine_text_status = old_status->text_status; - break; - case svn_wc_status_conflicted: - default: - /* ### Fetch compare data, or fall back to the documented - not retrieved behavior? */ - (*status)->pristine_text_status = svn_wc_status_none; - break; - } - - /* Find pristine_prop_status value */ - switch (old_status->prop_status) - { - case svn_wc_status_none: - case svn_wc_status_normal: - case svn_wc_status_modified: - if (old_status->node_status != svn_wc_status_added - && old_status->node_status != svn_wc_status_deleted - && old_status->node_status != svn_wc_status_replaced) - { - (*status)->pristine_prop_status = old_status->prop_status; - } - else - (*status)->pristine_prop_status = svn_wc_status_none; - break; - case svn_wc_status_conflicted: - default: - /* ### Fetch compare data, or fall back to the documented - not retrieved behavior? */ - (*status)->pristine_prop_status = svn_wc_status_none; - break; - } - - if (old_status->versioned - && old_status->conflicted - && old_status->node_status != svn_wc_status_obstructed - && (old_status->kind == svn_node_file - || old_status->node_status != svn_wc_status_missing)) - { - svn_boolean_t text_conflict_p, prop_conflict_p; - - /* The entry says there was a conflict, but the user might have - marked it as resolved by deleting the artifact files, so check - for that. */ - SVN_ERR(svn_wc__internal_conflicted_p(&text_conflict_p, - &prop_conflict_p, - NULL, - wc_ctx->db, local_abspath, - scratch_pool)); - - if (text_conflict_p) - (*status)->text_status = svn_wc_status_conflicted; - - if (prop_conflict_p) - (*status)->prop_status = svn_wc_status_conflicted; - } - - return SVN_NO_ERROR; -} - - -svn_error_t * svn_wc__fetch_kind_func(svn_node_kind_t *kind, void *baton, const char *path, diff --git a/subversion/libsvn_wc/wc-checks.h b/subversion/libsvn_wc/wc-checks.h index 58d2273..e867506 100644 --- a/subversion/libsvn_wc/wc-checks.h +++ b/subversion/libsvn_wc/wc-checks.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from wc-checks.sql and .dist_sandbox/subversion-1.8.13/subversion/libsvn_wc/token-map.h. +/* This file is automatically generated from wc-checks.sql and .dist_sandbox/subversion-1.9.7/subversion/libsvn_wc/token-map.h. * Do not edit this file -- edit the source and rerun gen-make.py */ #define STMT_VERIFICATION_TRIGGERS 0 @@ -42,14 +42,189 @@ "END; " \ "" +#define STMT_STATIC_VERIFY 1 +#define STMT_1_INFO {"STMT_STATIC_VERIFY", NULL} +#define STMT_1 \ + "SELECT local_relpath, op_depth, 1, 'Invalid parent relpath set in NODES' " \ + "FROM nodes n WHERE local_relpath != '' " \ + " AND (parent_relpath IS NULL " \ + " OR NOT (((local_relpath) > (CASE (parent_relpath) WHEN '' THEN '' ELSE (parent_relpath) || '/' END)) AND ((local_relpath) < CASE (parent_relpath) WHEN '' THEN X'FFFF' ELSE (parent_relpath) || '0' END)) " \ + " OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) " \ + "UNION ALL " \ + "SELECT local_relpath, -1, 2, 'Invalid parent relpath set in ACTUAL' " \ + "FROM actual_node a WHERE local_relpath != '' " \ + " AND (parent_relpath IS NULL " \ + " OR NOT (((local_relpath) > (CASE (parent_relpath) WHEN '' THEN '' ELSE (parent_relpath) || '/' END)) AND ((local_relpath) < CASE (parent_relpath) WHEN '' THEN X'FFFF' ELSE (parent_relpath) || '0' END)) " \ + " OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) " \ + "UNION ALL " \ + "SELECT local_relpath, -1, 10, 'No ancestor in ACTUAL' " \ + "FROM actual_node a WHERE local_relpath != '' " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.parent_relpath) " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.local_relpath) " \ + "UNION ALL " \ + "SELECT a.local_relpath, -1, 11, 'Bad or Unneeded actual data' " \ + "FROM actual_node a " \ + "LEFT JOIN nodes n on n.wc_id = a.wc_id AND n.local_relpath = a.local_relpath " \ + " AND n.op_depth = (SELECT MAX(op_depth) from nodes i " \ + " WHERE i.wc_id=a.wc_id AND i.local_relpath=a.local_relpath) " \ + "WHERE (a.properties IS NOT NULL " \ + " AND (n.presence IS NULL " \ + " OR n.presence NOT IN ('normal', 'incomplete'))) " \ + " OR (a.changelist IS NOT NULL AND (n.kind IS NOT NULL AND n.kind != 'file')) " \ + " OR (a.conflict_data IS NULL AND a.properties IS NULL AND a.changelist IS NULL) " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.parent_relpath) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 20, 'No ancestor in NODES' " \ + "FROM nodes n WHERE local_relpath != '' " \ + " AND file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=n.wc_id " \ + " AND i.local_relpath=n.parent_relpath " \ + " AND i.op_depth <= n.op_depth) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 21, 'Unneeded node data' " \ + "FROM nodes " \ + "WHERE presence NOT IN ('normal', 'incomplete') " \ + "AND (properties IS NOT NULL " \ + " OR checksum IS NOT NULL " \ + " OR depth IS NOT NULL " \ + " OR symlink_target IS NOT NULL " \ + " OR changed_revision IS NOT NULL " \ + " OR (changed_date IS NOT NULL AND changed_date != 0) " \ + " OR changed_author IS NOT NULL " \ + " OR translated_size IS NOT NULL " \ + " OR last_mod_time IS NOT NULL " \ + " OR dav_cache IS NOT NULL " \ + " OR file_external IS NOT NULL " \ + " OR inherited_props IS NOT NULL) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 22, 'Unneeded base-deleted node data' " \ + "FROM nodes " \ + "WHERE presence IN ('base-deleted') " \ + "AND (repos_id IS NOT NULL " \ + " OR repos_path IS NOT NULL " \ + " OR revision IS NOT NULL) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 23, 'Kind specific data invalid on normal' " \ + "FROM nodes " \ + "WHERE presence IN ('normal', 'incomplete') " \ + "AND (kind IS NULL " \ + " OR (repos_path IS NULL " \ + " AND (properties IS NOT NULL " \ + " OR changed_revision IS NOT NULL " \ + " OR changed_author IS NOT NULL " \ + " OR (changed_date IS NOT NULL AND changed_date != 0))) " \ + " OR (CASE WHEN kind = 'file' AND repos_path IS NOT NULL " \ + " THEN checksum IS NULL " \ + " ELSE checksum IS NOT NULL END) " \ + " OR (CASE WHEN kind = 'dir' THEN depth IS NULL " \ + " ELSE depth IS NOT NULL END) " \ + " OR (CASE WHEN kind = 'symlink' THEN symlink_target IS NULL " \ + " ELSE symlink_target IS NOT NULL END)) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 24, 'Invalid op-depth for local add' " \ + "FROM nodes " \ + "WHERE presence IN ('normal', 'incomplete') " \ + " AND repos_path IS NULL " \ + " AND op_depth != relpath_depth(local_relpath) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 25, 'Node missing op-depth ancestor' " \ + "FROM nodes n " \ + "WHERE op_depth < relpath_depth(local_relpath) " \ + " AND file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes p " \ + " WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath " \ + " AND p.op_depth=n.op_depth " \ + " AND (p.presence IN ('normal', 'incomplete') " \ + " OR (p.presence IN ('base-deleted', 'not-present') " \ + " AND n.presence = 'base-deleted'))) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 26, 'Copied descendant mismatch' " \ + "FROM nodes n " \ + "JOIN nodes p " \ + " ON p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath " \ + " AND n.op_depth=p.op_depth " \ + "WHERE n.op_depth > 0 AND n.presence IN ('normal', 'incomplete') " \ + " AND (n.repos_id != p.repos_id " \ + " OR n.repos_path != " \ + " (CASE WHEN (n.parent_relpath) = '' THEN (CASE WHEN (p.repos_path) = '' THEN (n.local_relpath) WHEN (n.local_relpath) = '' THEN (p.repos_path) ELSE (p.repos_path) || '/' || (n.local_relpath) END) WHEN (p.repos_path) = '' THEN (CASE WHEN (n.parent_relpath) = '' THEN (n.local_relpath) WHEN SUBSTR((n.local_relpath), 1, LENGTH(n.parent_relpath)) = (n.parent_relpath) THEN CASE WHEN LENGTH(n.parent_relpath) = LENGTH(n.local_relpath) THEN '' WHEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1, 1) = '/' THEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+2) END END) WHEN SUBSTR((n.local_relpath), 1, LENGTH(n.parent_relpath)) = (n.parent_relpath) THEN CASE WHEN LENGTH(n.parent_relpath) = LENGTH(n.local_relpath) THEN (p.repos_path) WHEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1, 1) = '/' THEN (p.repos_path) || SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1) END END) " \ + " OR n.revision != p.revision " \ + " OR p.kind != 'dir' " \ + " OR n.moved_here IS NOT p.moved_here) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 27, 'Invalid op-root presence' " \ + "FROM nodes n " \ + "WHERE n.op_depth = relpath_depth(local_relpath) " \ + " AND presence NOT IN ('normal', 'incomplete', 'base-deleted') " \ + "UNION ALL " \ + "SELECT n.local_relpath, s.op_depth, 28, 'Incomplete shadowing' " \ + "FROM nodes n " \ + "JOIN nodes s ON s.wc_id=n.wc_id AND s.local_relpath=n.local_relpath " \ + " AND s.op_depth = relpath_depth(s.local_relpath) " \ + " AND s.op_depth = (SELECT MIN(op_depth) FROM nodes d " \ + " WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath " \ + " AND d.op_depth > n.op_depth) " \ + "WHERE n.presence IN ('normal', 'incomplete') " \ + " AND EXISTS(SELECT 1 " \ + " FROM nodes dn " \ + " WHERE dn.wc_id=n.wc_id AND dn.op_depth=n.op_depth " \ + " AND dn.presence IN ('normal', 'incomplete') " \ + " AND (((dn.local_relpath) > (CASE (n.local_relpath) WHEN '' THEN '' ELSE (n.local_relpath) || '/' END)) AND ((dn.local_relpath) < CASE (n.local_relpath) WHEN '' THEN X'FFFF' ELSE (n.local_relpath) || '0' END)) " \ + " AND dn.file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 " \ + " FROM nodes ds " \ + " WHERE ds.wc_id=n.wc_id AND ds.op_depth=s.op_depth " \ + " AND ds.local_relpath=dn.local_relpath)) " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.op_depth, 29, 'Invalid base-delete' " \ + "FROM nodes s " \ + "LEFT JOIN nodes n ON n.wc_id=s.wc_id AND n.local_relpath=s.local_relpath " \ + " AND n.op_depth = (SELECT MAX(op_depth) FROM nodes d " \ + " WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath " \ + " AND d.op_depth < s.op_depth) " \ + "WHERE s.presence = 'base-deleted' " \ + " AND (n.presence IS NULL " \ + " OR n.presence NOT IN ('normal', 'incomplete') " \ + " ) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 30, 'Invalid data for BASE' " \ + "FROM nodes n " \ + "WHERE n.op_depth = 0 " \ + " AND (n.moved_to IS NOT NULL " \ + " OR n.moved_here IS NOT NULL) " \ + "UNION ALL " \ + "SELECT d.local_relpath, d.op_depth, 60, 'Moved here without origin' " \ + "FROM nodes d " \ + "WHERE d.op_depth = relpath_depth(d.local_relpath) " \ + " AND d.moved_here IS NOT NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes s " \ + " WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath) " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.op_depth, 61, 'Moved to without target' " \ + "FROM nodes s " \ + "WHERE s.moved_to IS NOT NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes d " \ + " WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to " \ + " AND d.op_depth = relpath_depth(d.local_relpath) " \ + " AND d.moved_here =1 AND d.repos_path IS NOT NULL) " \ + "" + #define WC_CHECKS_SQL_DECLARE_STATEMENTS(varname) \ static const char * const varname[] = { \ STMT_0, \ + STMT_1, \ NULL \ } #define WC_CHECKS_SQL_DECLARE_STATEMENT_INFO(varname) \ static const char * const varname[][2] = { \ STMT_0_INFO, \ + STMT_1_INFO, \ {NULL, NULL} \ } diff --git a/subversion/libsvn_wc/wc-checks.sql b/subversion/libsvn_wc/wc-checks.sql index a677270..fce7b49 100644 --- a/subversion/libsvn_wc/wc-checks.sql +++ b/subversion/libsvn_wc/wc-checks.sql @@ -75,3 +75,217 @@ BEGIN SELECT RAISE(FAIL, 'WC DB validity check 04 failed'); END; +-- STMT_STATIC_VERIFY +SELECT local_relpath, op_depth, 1, 'Invalid parent relpath set in NODES' +FROM nodes n WHERE local_relpath != '' + AND (parent_relpath IS NULL + OR NOT IS_STRICT_DESCENDANT_OF(local_relpath, parent_relpath) + OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) + +UNION ALL + +SELECT local_relpath, -1, 2, 'Invalid parent relpath set in ACTUAL' +FROM actual_node a WHERE local_relpath != '' + AND (parent_relpath IS NULL + OR NOT IS_STRICT_DESCENDANT_OF(local_relpath, parent_relpath) + OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) + +UNION ALL + +/* All ACTUAL nodes must have an equivalent NODE in NODES + or be only one level deep (delete-delete tc) */ +SELECT local_relpath, -1, 10, 'No ancestor in ACTUAL' +FROM actual_node a WHERE local_relpath != '' + AND NOT EXISTS(SELECT 1 from nodes i + WHERE i.wc_id=a.wc_id + AND i.local_relpath=a.parent_relpath) + AND NOT EXISTS(SELECT 1 from nodes i + WHERE i.wc_id=a.wc_id + AND i.local_relpath=a.local_relpath) + +UNION ALL +/* Verify if the ACTUAL data makes sense for the related node. + Only conflict data is valid if there is none */ +SELECT a.local_relpath, -1, 11, 'Bad or Unneeded actual data' +FROM actual_node a +LEFT JOIN nodes n on n.wc_id = a.wc_id AND n.local_relpath = a.local_relpath + AND n.op_depth = (SELECT MAX(op_depth) from nodes i + WHERE i.wc_id=a.wc_id AND i.local_relpath=a.local_relpath) +WHERE (a.properties IS NOT NULL + AND (n.presence IS NULL + OR n.presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE))) + OR (a.changelist IS NOT NULL AND (n.kind IS NOT NULL AND n.kind != MAP_FILE)) + OR (a.conflict_data IS NULL AND a.properties IS NULL AND a.changelist IS NULL) + AND NOT EXISTS(SELECT 1 from nodes i + WHERE i.wc_id=a.wc_id + AND i.local_relpath=a.parent_relpath) + +UNION ALL + +/* A parent node must exist for every normal node except the root. + That node must exist at a lower or equal op-depth */ +SELECT local_relpath, op_depth, 20, 'No ancestor in NODES' +FROM nodes n WHERE local_relpath != '' + AND file_external IS NULL + AND NOT EXISTS(SELECT 1 from nodes i + WHERE i.wc_id=n.wc_id + AND i.local_relpath=n.parent_relpath + AND i.op_depth <= n.op_depth) + +UNION ALL +/* If a node is not present in the working copy (normal, add, copy) it doesn't + have revision details stored on this record */ +SELECT local_relpath, op_depth, 21, 'Unneeded node data' +FROM nodes +WHERE presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE) +AND (properties IS NOT NULL + OR checksum IS NOT NULL + OR depth IS NOT NULL + OR symlink_target IS NOT NULL + OR changed_revision IS NOT NULL + OR (changed_date IS NOT NULL AND changed_date != 0) + OR changed_author IS NOT NULL + OR translated_size IS NOT NULL + OR last_mod_time IS NOT NULL + OR dav_cache IS NOT NULL + OR file_external IS NOT NULL + OR inherited_props IS NOT NULL) + +UNION ALL +/* base-deleted nodes don't have a repository location. They are just + shadowing without a replacement */ +SELECT local_relpath, op_depth, 22, 'Unneeded base-deleted node data' +FROM nodes +WHERE presence IN (MAP_BASE_DELETED) +AND (repos_id IS NOT NULL + OR repos_path IS NOT NULL + OR revision IS NOT NULL) + +UNION ALL +/* Verify if type specific data is set (or not set for wrong type) */ +SELECT local_relpath, op_depth, 23, 'Kind specific data invalid on normal' +FROM nodes +WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE) +AND (kind IS NULL + OR (repos_path IS NULL + AND (properties IS NOT NULL + OR changed_revision IS NOT NULL + OR changed_author IS NOT NULL + OR (changed_date IS NOT NULL AND changed_date != 0))) + OR (CASE WHEN kind = MAP_FILE AND repos_path IS NOT NULL + THEN checksum IS NULL + ELSE checksum IS NOT NULL END) + OR (CASE WHEN kind = MAP_DIR THEN depth IS NULL + ELSE depth IS NOT NULL END) + OR (CASE WHEN kind = MAP_SYMLINK THEN symlink_target IS NULL + ELSE symlink_target IS NOT NULL END)) + +UNION ALL +/* Local-adds are always their own operation (read: they don't have + op-depth descendants, nor are op-depth descendants */ +SELECT local_relpath, op_depth, 24, 'Invalid op-depth for local add' +FROM nodes +WHERE presence IN (MAP_NORMAL, MAP_INCOMPLETE) + AND repos_path IS NULL + AND op_depth != relpath_depth(local_relpath) + +UNION ALL +/* op-depth descendants are only valid if they have a direct parent + node at the same op-depth. Only certain types allow further + descendants */ +SELECT local_relpath, op_depth, 25, 'Node missing op-depth ancestor' +FROM nodes n +WHERE op_depth < relpath_depth(local_relpath) + AND file_external IS NULL + AND NOT EXISTS(SELECT 1 FROM nodes p + WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath + AND p.op_depth=n.op_depth + AND (p.presence IN (MAP_NORMAL, MAP_INCOMPLETE) + OR (p.presence IN (MAP_BASE_DELETED, MAP_NOT_PRESENT) + AND n.presence = MAP_BASE_DELETED))) + +UNION ALL +/* Present op-depth descendants have the repository location implied by their + ancestor */ +SELECT n.local_relpath, n.op_depth, 26, 'Copied descendant mismatch' +FROM nodes n +JOIN nodes p + ON p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath + AND n.op_depth=p.op_depth +WHERE n.op_depth > 0 AND n.presence IN (MAP_NORMAL, MAP_INCOMPLETE) + AND (n.repos_id != p.repos_id + OR n.repos_path != + RELPATH_SKIP_JOIN(n.parent_relpath, p.repos_path, n.local_relpath) + OR n.revision != p.revision + OR p.kind != MAP_DIR + OR n.moved_here IS NOT p.moved_here) + +UNION ALL +/* Only certain presence values are valid as op-root. + Note that the wc-root always has presence normal or incomplete */ +SELECT n.local_relpath, n.op_depth, 27, 'Invalid op-root presence' +FROM nodes n +WHERE n.op_depth = relpath_depth(local_relpath) + AND presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE, MAP_BASE_DELETED) + +UNION ALL +/* If a node is shadowed, all its present op-depth descendants + must be shadowed at the same op-depth as well */ +SELECT n.local_relpath, s.op_depth, 28, 'Incomplete shadowing' +FROM nodes n +JOIN nodes s ON s.wc_id=n.wc_id AND s.local_relpath=n.local_relpath + AND s.op_depth = relpath_depth(s.local_relpath) + AND s.op_depth = (SELECT MIN(op_depth) FROM nodes d + WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath + AND d.op_depth > n.op_depth) +WHERE n.presence IN (MAP_NORMAL, MAP_INCOMPLETE) + AND EXISTS(SELECT 1 + FROM nodes dn + WHERE dn.wc_id=n.wc_id AND dn.op_depth=n.op_depth + AND dn.presence IN (MAP_NORMAL, MAP_INCOMPLETE) + AND IS_STRICT_DESCENDANT_OF(dn.local_relpath, n.local_relpath) + AND dn.file_external IS NULL + AND NOT EXISTS(SELECT 1 + FROM nodes ds + WHERE ds.wc_id=n.wc_id AND ds.op_depth=s.op_depth + AND ds.local_relpath=dn.local_relpath)) + +UNION ALL +/* A base-delete is only valid if it directly deletes a present node */ +SELECT s.local_relpath, s.op_depth, 29, 'Invalid base-delete' +FROM nodes s +LEFT JOIN nodes n ON n.wc_id=s.wc_id AND n.local_relpath=s.local_relpath + AND n.op_depth = (SELECT MAX(op_depth) FROM nodes d + WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath + AND d.op_depth < s.op_depth) +WHERE s.presence = MAP_BASE_DELETED + AND (n.presence IS NULL + OR n.presence NOT IN (MAP_NORMAL, MAP_INCOMPLETE) + /*OR n.kind != s.kind*/) + +UNION ALL +/* Moves are stored in the working layers, not in BASE */ +SELECT n.local_relpath, n.op_depth, 30, 'Invalid data for BASE' +FROM nodes n +WHERE n.op_depth = 0 + AND (n.moved_to IS NOT NULL + OR n.moved_here IS NOT NULL) + +UNION ALL +/* If moved_here is set on an op-root, there must be a proper moved_to */ +SELECT d.local_relpath, d.op_depth, 60, 'Moved here without origin' +FROM nodes d +WHERE d.op_depth = relpath_depth(d.local_relpath) + AND d.moved_here IS NOT NULL + AND NOT EXISTS(SELECT 1 FROM nodes s + WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath) + +UNION ALL +/* If moved_to is set there should be an moved op root at the target */ +SELECT s.local_relpath, s.op_depth, 61, 'Moved to without target' +FROM nodes s +WHERE s.moved_to IS NOT NULL + AND NOT EXISTS(SELECT 1 FROM nodes d + WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to + AND d.op_depth = relpath_depth(d.local_relpath) + AND d.moved_here =1 AND d.repos_path IS NOT NULL) diff --git a/subversion/libsvn_wc/wc-metadata.h b/subversion/libsvn_wc/wc-metadata.h index 25e392a..f548d91 100644 --- a/subversion/libsvn_wc/wc-metadata.h +++ b/subversion/libsvn_wc/wc-metadata.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from wc-metadata.sql and .dist_sandbox/subversion-1.8.13/subversion/libsvn_wc/token-map.h. +/* This file is automatically generated from wc-metadata.sql and .dist_sandbox/subversion-1.9.7/subversion/libsvn_wc/token-map.h. * Do not edit this file -- edit the source and rerun gen-make.py */ #define STMT_CREATE_SCHEMA 0 @@ -164,21 +164,25 @@ #define STMT_4 \ "ANALYZE sqlite_master; " \ "DELETE FROM sqlite_stat1 " \ - "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK', 'EXTERNALS'); " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('NODES', 'sqlite_autoindex_NODES_1', '8000 8000 2 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('NODES', 'I_NODES_PARENT', '8000 8000 10 2 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('NODES', 'I_NODES_MOVED', '8000 8000 1 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('ACTUAL_NODE', 'sqlite_autoindex_ACTUAL_NODE_1', '8000 8000 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('ACTUAL_NODE', 'I_ACTUAL_PARENT', '8000 8000 10 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('LOCK', 'sqlite_autoindex_LOCK_1', '100 100 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('WC_LOCK', 'sqlite_autoindex_WC_LOCK_1', '100 100 1'); " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + " ('EXTERNALS','sqlite_autoindex_EXTERNALS_1', '100 100 1'); " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + " ('EXTERNALS','I_EXTERNALS_DEFINED', '100 100 3 1'); " \ "ANALYZE sqlite_master; " \ "" diff --git a/subversion/libsvn_wc/wc-metadata.sql b/subversion/libsvn_wc/wc-metadata.sql index e4b226e..f52df93 100644 --- a/subversion/libsvn_wc/wc-metadata.sql +++ b/subversion/libsvn_wc/wc-metadata.sql @@ -108,7 +108,7 @@ CREATE TABLE PRISTINE ( ); CREATE INDEX I_PRISTINE_MD5 ON PRISTINE (md5_checksum); - + /* ------------------------------------------------------------------------- */ /* The ACTUAL_NODE table describes text changes and property changes @@ -150,7 +150,7 @@ CREATE TABLE ACTUAL_NODE ( /* if not NULL, this node is part of a changelist. */ changelist TEXT, - + /* ### need to determine values. "unknown" (no info), "admin" (they ### used something like 'svn edit'), "noticed" (saw a mod while ### scanning the filesystem). */ @@ -170,7 +170,7 @@ CREATE TABLE ACTUAL_NODE ( /* stsp: This is meant for text conflicts, right? What about property conflicts? Why do we need these in a column to refer to the pristine store? Can't we just parse the checksums from - conflict_data as well? + conflict_data as well? rhuijben: Because that won't allow triggers to handle refcounts. We would have to scan all conflict skels before cleaning up the a single file from the pristine stor */ @@ -200,7 +200,7 @@ CREATE TABLE LOCK ( lock_owner TEXT, lock_comment TEXT, lock_date INTEGER, /* an APR date/time (usec since 1970) */ - + PRIMARY KEY (repos_id, repos_relpath) ); @@ -553,7 +553,7 @@ CREATE TABLE EXTERNALS ( /* the kind of the external. */ kind TEXT NOT NULL, - /* The local relpath of the directory NODE defining this external + /* The local relpath of the directory NODE defining this external (Defaults to the parent directory of the file external after upgrade) */ def_local_relpath TEXT NOT NULL, @@ -577,7 +577,7 @@ CREATE UNIQUE INDEX I_EXTERNALS_DEFINED ON EXTERNALS (wc_id, indexes to make better decisions in the query planner. For every interesting index this contains a number of rows where the - statistics ar calculated for and then for every column in the index the + statistics are calculated for and then for every column in the index the average number of rows with the same value in all columns left of this column including the column itself. @@ -598,27 +598,32 @@ CREATE UNIQUE INDEX I_EXTERNALS_DEFINED ON EXTERNALS (wc_id, ANALYZE sqlite_master; /* Creates empty sqlite_stat1 if necessary */ DELETE FROM sqlite_stat1 -WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK'); +WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK', 'EXTERNALS'); -INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES ('NODES', 'sqlite_autoindex_NODES_1', '8000 8000 2 1'); -INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES ('NODES', 'I_NODES_PARENT', '8000 8000 10 2 1'); /* Tell a lie: We ignore that 99.9% of all moved_to values are NULL */ -INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES ('NODES', 'I_NODES_MOVED', '8000 8000 1 1'); -INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES ('ACTUAL_NODE', 'sqlite_autoindex_ACTUAL_NODE_1', '8000 8000 1'); -INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES ('ACTUAL_NODE', 'I_ACTUAL_PARENT', '8000 8000 10 1'); -INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES ('LOCK', 'sqlite_autoindex_LOCK_1', '100 100 1'); -INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES ('WC_LOCK', 'sqlite_autoindex_WC_LOCK_1', '100 100 1'); +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES + ('EXTERNALS','sqlite_autoindex_EXTERNALS_1', '100 100 1'); +INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES + ('EXTERNALS','I_EXTERNALS_DEFINED', '100 100 3 1'); + /* sqlite_autoindex_WORK_QUEUE_1 doesn't exist because WORK_QUEUE is a INTEGER PRIMARY KEY AUTOINCREMENT table */ @@ -774,7 +779,7 @@ LIMIT 1 /* ------------------------------------------------------------------------- */ -/* Format 28 involves no schema changes, it only converts MD5 pristine +/* Format 28 involves no schema changes, it only converts MD5 pristine references to SHA1. */ -- STMT_UPGRADE_TO_28 diff --git a/subversion/libsvn_wc/wc-queries.h b/subversion/libsvn_wc/wc-queries.h index e33af98..2a75940 100644 --- a/subversion/libsvn_wc/wc-queries.h +++ b/subversion/libsvn_wc/wc-queries.h @@ -1,4 +1,4 @@ -/* This file is automatically generated from wc-queries.sql and .dist_sandbox/subversion-1.8.13/subversion/libsvn_wc/token-map.h. +/* This file is automatically generated from wc-queries.sql and .dist_sandbox/subversion-1.9.7/subversion/libsvn_wc/token-map.h. * Do not edit this file -- edit the source and rerun gen-make.py */ #define STMT_SELECT_NODE_INFO 0 @@ -55,6 +55,15 @@ #define STMT_4_INFO {"STMT_SELECT_BASE_CHILDREN_INFO", NULL} #define STMT_4 \ "SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind, " \ + " revision, depth, file_external " \ + "FROM nodes " \ + "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 " \ + "" + +#define STMT_SELECT_BASE_CHILDREN_INFO_LOCK 5 +#define STMT_5_INFO {"STMT_SELECT_BASE_CHILDREN_INFO_LOCK", NULL} +#define STMT_5 \ + "SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind, " \ " revision, depth, file_external, " \ " lock_token, lock_owner, lock_comment, lock_date " \ "FROM nodes " \ @@ -63,9 +72,9 @@ "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_SELECT_WORKING_NODE 5 -#define STMT_5_INFO {"STMT_SELECT_WORKING_NODE", NULL} -#define STMT_5 \ +#define STMT_SELECT_WORKING_NODE 6 +#define STMT_6_INFO {"STMT_SELECT_WORKING_NODE", NULL} +#define STMT_6 \ "SELECT op_depth, presence, kind, checksum, translated_size, " \ " changed_revision, changed_date, changed_author, depth, symlink_target, " \ " repos_id, repos_path, revision, " \ @@ -76,19 +85,19 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_DEPTH_NODE 6 -#define STMT_6_INFO {"STMT_SELECT_DEPTH_NODE", NULL} -#define STMT_6 \ +#define STMT_SELECT_DEPTH_NODE 7 +#define STMT_7_INFO {"STMT_SELECT_DEPTH_NODE", NULL} +#define STMT_7 \ "SELECT repos_id, repos_path, presence, kind, revision, checksum, " \ " translated_size, changed_revision, changed_date, changed_author, depth, " \ - " symlink_target, last_mod_time, properties " \ + " symlink_target, properties, moved_to, moved_here " \ "FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ "" -#define STMT_SELECT_LOWEST_WORKING_NODE 7 -#define STMT_7_INFO {"STMT_SELECT_LOWEST_WORKING_NODE", NULL} -#define STMT_7 \ +#define STMT_SELECT_LOWEST_WORKING_NODE 8 +#define STMT_8_INFO {"STMT_SELECT_LOWEST_WORKING_NODE", NULL} +#define STMT_8 \ "SELECT op_depth, presence, kind, moved_to " \ "FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 " \ @@ -96,9 +105,9 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_HIGHEST_WORKING_NODE 8 -#define STMT_8_INFO {"STMT_SELECT_HIGHEST_WORKING_NODE", NULL} -#define STMT_8 \ +#define STMT_SELECT_HIGHEST_WORKING_NODE 9 +#define STMT_9_INFO {"STMT_SELECT_HIGHEST_WORKING_NODE", NULL} +#define STMT_9 \ "SELECT op_depth " \ "FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth < ?3 " \ @@ -106,17 +115,17 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_ACTUAL_NODE 9 -#define STMT_9_INFO {"STMT_SELECT_ACTUAL_NODE", NULL} -#define STMT_9 \ +#define STMT_SELECT_ACTUAL_NODE 10 +#define STMT_10_INFO {"STMT_SELECT_ACTUAL_NODE", NULL} +#define STMT_10 \ "SELECT changelist, properties, conflict_data " \ "FROM actual_node " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_SELECT_NODE_CHILDREN_INFO 10 -#define STMT_10_INFO {"STMT_SELECT_NODE_CHILDREN_INFO", NULL} -#define STMT_10 \ +#define STMT_SELECT_NODE_CHILDREN_INFO 11 +#define STMT_11_INFO {"STMT_SELECT_NODE_CHILDREN_INFO", NULL} +#define STMT_11 \ "SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision, " \ " checksum, translated_size, changed_revision, changed_date, changed_author, " \ " depth, symlink_target, last_mod_time, properties, lock_token, lock_owner, " \ @@ -125,51 +134,67 @@ "LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id " \ " AND nodes.repos_path = lock.repos_relpath AND op_depth = 0 " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ + "ORDER BY local_relpath DESC, op_depth DESC " \ "" -#define STMT_SELECT_NODE_CHILDREN_WALKER_INFO 11 -#define STMT_11_INFO {"STMT_SELECT_NODE_CHILDREN_WALKER_INFO", NULL} -#define STMT_11 \ +#define STMT_SELECT_BASE_NODE_CHILDREN_INFO 12 +#define STMT_12_INFO {"STMT_SELECT_BASE_NODE_CHILDREN_INFO", NULL} +#define STMT_12 \ + "SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision, " \ + " checksum, translated_size, changed_revision, changed_date, changed_author, " \ + " depth, symlink_target, last_mod_time, properties, lock_token, lock_owner, " \ + " lock_comment, lock_date, local_relpath, moved_here, moved_to, file_external " \ + "FROM nodes " \ + "LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id " \ + " AND nodes.repos_path = lock.repos_relpath AND op_depth = 0 " \ + "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 " \ + "ORDER BY local_relpath DESC " \ + "" + +#define STMT_SELECT_NODE_CHILDREN_WALKER_INFO 13 +#define STMT_13_INFO {"STMT_SELECT_NODE_CHILDREN_WALKER_INFO", NULL} +#define STMT_13 \ "SELECT local_relpath, op_depth, presence, kind " \ "FROM nodes_current " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ + "ORDER BY local_relpath " \ "" -#define STMT_SELECT_ACTUAL_CHILDREN_INFO 12 -#define STMT_12_INFO {"STMT_SELECT_ACTUAL_CHILDREN_INFO", NULL} -#define STMT_12 \ +#define STMT_SELECT_ACTUAL_CHILDREN_INFO 14 +#define STMT_14_INFO {"STMT_SELECT_ACTUAL_CHILDREN_INFO", NULL} +#define STMT_14 \ "SELECT local_relpath, changelist, properties, conflict_data " \ "FROM actual_node " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ "" -#define STMT_SELECT_REPOSITORY_BY_ID 13 -#define STMT_13_INFO {"STMT_SELECT_REPOSITORY_BY_ID", NULL} -#define STMT_13 \ +#define STMT_SELECT_REPOSITORY_BY_ID 15 +#define STMT_15_INFO {"STMT_SELECT_REPOSITORY_BY_ID", NULL} +#define STMT_15 \ "SELECT root, uuid FROM repository WHERE id = ?1 " \ "" -#define STMT_SELECT_WCROOT_NULL 14 -#define STMT_14_INFO {"STMT_SELECT_WCROOT_NULL", NULL} -#define STMT_14 \ +#define STMT_SELECT_WCROOT_NULL 16 +#define STMT_16_INFO {"STMT_SELECT_WCROOT_NULL", NULL} +#define STMT_16 \ "SELECT id FROM wcroot WHERE local_abspath IS NULL " \ "" -#define STMT_SELECT_REPOSITORY 15 -#define STMT_15_INFO {"STMT_SELECT_REPOSITORY", NULL} -#define STMT_15 \ +#define STMT_SELECT_REPOSITORY 17 +#define STMT_17_INFO {"STMT_SELECT_REPOSITORY", NULL} +#define STMT_17 \ "SELECT id FROM repository WHERE root = ?1 " \ "" -#define STMT_INSERT_REPOSITORY 16 -#define STMT_16_INFO {"STMT_INSERT_REPOSITORY", NULL} -#define STMT_16 \ +#define STMT_INSERT_REPOSITORY 18 +#define STMT_18_INFO {"STMT_INSERT_REPOSITORY", NULL} +#define STMT_18 \ "INSERT INTO repository (root, uuid) VALUES (?1, ?2) " \ "" -#define STMT_INSERT_NODE 17 -#define STMT_17_INFO {"STMT_INSERT_NODE", NULL} -#define STMT_17 \ +#define STMT_INSERT_NODE 19 +#define STMT_19_INFO {"STMT_INSERT_NODE", NULL} +#define STMT_19 \ "INSERT OR REPLACE INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \ " revision, presence, depth, kind, changed_revision, changed_date, " \ @@ -180,22 +205,9 @@ " ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23) " \ "" -#define STMT_SELECT_BASE_PRESENT 18 -#define STMT_18_INFO {"STMT_SELECT_BASE_PRESENT", NULL} -#define STMT_18 \ - "SELECT local_relpath, kind FROM nodes n " \ - "WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ - " AND op_depth = 0 " \ - " AND presence in ('normal', 'incomplete') " \ - " AND NOT EXISTS(SELECT 1 FROM NODES w " \ - " WHERE w.wc_id = ?1 AND w.local_relpath = n.local_relpath " \ - " AND op_depth > 0) " \ - "ORDER BY local_relpath DESC " \ - "" - -#define STMT_SELECT_WORKING_PRESENT 19 -#define STMT_19_INFO {"STMT_SELECT_WORKING_PRESENT", NULL} -#define STMT_19 \ +#define STMT_SELECT_WORKING_PRESENT 20 +#define STMT_20_INFO {"STMT_SELECT_WORKING_PRESENT", NULL} +#define STMT_20 \ "SELECT local_relpath, kind, checksum, translated_size, last_mod_time " \ "FROM nodes n " \ "WHERE wc_id = ?1 " \ @@ -208,25 +220,25 @@ "ORDER BY local_relpath DESC " \ "" -#define STMT_DELETE_NODE_RECURSIVE 20 -#define STMT_20_INFO {"STMT_DELETE_NODE_RECURSIVE", NULL} -#define STMT_20 \ +#define STMT_DELETE_NODE_RECURSIVE 21 +#define STMT_21_INFO {"STMT_DELETE_NODE_RECURSIVE", NULL} +#define STMT_21 \ "DELETE FROM NODES " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_DELETE_NODE 21 -#define STMT_21_INFO {"STMT_DELETE_NODE", NULL} -#define STMT_21 \ +#define STMT_DELETE_NODE 22 +#define STMT_22_INFO {"STMT_DELETE_NODE", NULL} +#define STMT_22 \ "DELETE " \ "FROM NODES " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ "" -#define STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE 22 -#define STMT_22_INFO {"STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE", NULL} -#define STMT_22 \ +#define STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE 23 +#define STMT_23_INFO {"STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE", NULL} +#define STMT_23 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ " AND EXISTS(SELECT 1 FROM NODES b " \ @@ -240,113 +252,161 @@ " AND presence in ('normal', 'incomplete', 'not-present')) " \ "" -#define STMT_DELETE_WORKING_BASE_DELETE 23 -#define STMT_23_INFO {"STMT_DELETE_WORKING_BASE_DELETE", NULL} -#define STMT_23 \ +#define STMT_DELETE_WORKING_BASE_DELETE 24 +#define STMT_24_INFO {"STMT_DELETE_WORKING_BASE_DELETE", NULL} +#define STMT_24 \ "DELETE FROM nodes " \ - "WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + "WHERE wc_id = ?1 AND local_relpath = ?2 " \ " AND presence = 'base-deleted' " \ - " AND op_depth > 0 " \ + " AND op_depth > ?3 " \ " AND op_depth = (SELECT MIN(op_depth) FROM nodes n " \ " WHERE n.wc_id = ?1 " \ " AND n.local_relpath = nodes.local_relpath " \ - " AND op_depth > 0) " \ + " AND op_depth > ?3) " \ "" -#define STMT_DELETE_WORKING_RECURSIVE 24 -#define STMT_24_INFO {"STMT_DELETE_WORKING_RECURSIVE", NULL} -#define STMT_24 \ - "DELETE FROM nodes " \ - "WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ - " AND op_depth > 0 " \ - "" - -#define STMT_DELETE_BASE_RECURSIVE 25 -#define STMT_25_INFO {"STMT_DELETE_BASE_RECURSIVE", NULL} +#define STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE 25 +#define STMT_25_INFO {"STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE", NULL} #define STMT_25 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ - " AND op_depth = 0 " \ + " AND presence = 'base-deleted' " \ + " AND op_depth > ?3 " \ + " AND op_depth = (SELECT MIN(op_depth) FROM nodes n " \ + " WHERE n.wc_id = ?1 " \ + " AND n.local_relpath = nodes.local_relpath " \ + " AND op_depth > ?3) " \ "" -#define STMT_DELETE_WORKING_OP_DEPTH 26 -#define STMT_26_INFO {"STMT_DELETE_WORKING_OP_DEPTH", NULL} +#define STMT_DELETE_WORKING_RECURSIVE 26 +#define STMT_26_INFO {"STMT_DELETE_WORKING_RECURSIVE", NULL} #define STMT_26 \ "DELETE FROM nodes " \ - "WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ - " AND op_depth = ?3 " \ + "WHERE wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + " AND op_depth > 0 " \ "" -#define STMT_DELETE_WORKING_OP_DEPTH_ABOVE 27 -#define STMT_27_INFO {"STMT_DELETE_WORKING_OP_DEPTH_ABOVE", NULL} +#define STMT_DELETE_BASE_RECURSIVE 27 +#define STMT_27_INFO {"STMT_DELETE_BASE_RECURSIVE", NULL} #define STMT_27 \ "DELETE FROM nodes " \ - "WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ - " AND op_depth > ?3 " \ + "WHERE wc_id = ?1 AND (local_relpath = ?2 " \ + " OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ + " AND op_depth = 0 " \ "" -#define STMT_SELECT_LOCAL_RELPATH_OP_DEPTH 28 -#define STMT_28_INFO {"STMT_SELECT_LOCAL_RELPATH_OP_DEPTH", NULL} +#define STMT_DELETE_WORKING_OP_DEPTH 28 +#define STMT_28_INFO {"STMT_DELETE_WORKING_OP_DEPTH", NULL} #define STMT_28 \ - "SELECT local_relpath " \ - "FROM nodes " \ + "DELETE FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ " AND op_depth = ?3 " \ "" -#define STMT_SELECT_CHILDREN_OP_DEPTH 29 -#define STMT_29_INFO {"STMT_SELECT_CHILDREN_OP_DEPTH", NULL} +#define STMT_SELECT_LAYER_FOR_REPLACE 29 +#define STMT_29_INFO {"STMT_SELECT_LAYER_FOR_REPLACE", NULL} #define STMT_29 \ + "SELECT s.local_relpath, s.kind, " \ + " (CASE WHEN (?2) = '' THEN (CASE WHEN (?4) = '' THEN (s.local_relpath) WHEN (s.local_relpath) = '' THEN (?4) ELSE (?4) || '/' || (s.local_relpath) END) WHEN (?4) = '' THEN (CASE WHEN (?2) = '' THEN (s.local_relpath) WHEN SUBSTR((s.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(s.local_relpath) THEN '' WHEN SUBSTR((s.local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((s.local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((s.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(s.local_relpath) THEN (?4) WHEN SUBSTR((s.local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?4) || SUBSTR((s.local_relpath), LENGTH(?2)+1) END END) drp, 'normal' " \ + "FROM nodes s " \ + "WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3 " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.kind, " \ + " (CASE WHEN (?2) = '' THEN (CASE WHEN (?4) = '' THEN (s.local_relpath) WHEN (s.local_relpath) = '' THEN (?4) ELSE (?4) || '/' || (s.local_relpath) END) WHEN (?4) = '' THEN (CASE WHEN (?2) = '' THEN (s.local_relpath) WHEN SUBSTR((s.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(s.local_relpath) THEN '' WHEN SUBSTR((s.local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((s.local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((s.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(s.local_relpath) THEN (?4) WHEN SUBSTR((s.local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?4) || SUBSTR((s.local_relpath), LENGTH(?2)+1) END END) drp, d.presence " \ + "FROM nodes s " \ + "LEFT OUTER JOIN nodes d ON d.wc_id= ?1 AND d.op_depth = ?5 " \ + " AND d.local_relpath = drp " \ + "WHERE s.wc_id = ?1 " \ + " AND (((s.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((s.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + " AND s.op_depth = ?3 " \ + "ORDER BY s.local_relpath " \ + "" + +#define STMT_SELECT_DESCENDANTS_OP_DEPTH_RV 30 +#define STMT_30_INFO {"STMT_SELECT_DESCENDANTS_OP_DEPTH_RV", NULL} +#define STMT_30 \ "SELECT local_relpath, kind " \ "FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ " AND op_depth = ?3 " \ + " AND presence in ('normal', 'incomplete') " \ "ORDER BY local_relpath DESC " \ "" -#define STMT_COPY_NODE_MOVE 30 -#define STMT_30_INFO {"STMT_COPY_NODE_MOVE", NULL} -#define STMT_30 \ +#define STMT_COPY_NODE_MOVE 31 +#define STMT_31_INFO {"STMT_COPY_NODE_MOVE", NULL} +#define STMT_31 \ "INSERT OR REPLACE INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \ " revision, presence, depth, kind, changed_revision, changed_date, " \ " changed_author, checksum, properties, translated_size, last_mod_time, " \ " symlink_target, moved_here, moved_to ) " \ "SELECT " \ - " wc_id, ?4 , ?5 , ?6 , " \ - " repos_id, " \ - " repos_path, revision, presence, depth, kind, changed_revision, " \ - " changed_date, changed_author, checksum, properties, translated_size, " \ - " last_mod_time, symlink_target, 1, " \ - " (SELECT dst.moved_to FROM nodes AS dst " \ - " WHERE dst.wc_id = ?1 " \ - " AND dst.local_relpath = ?4 " \ - " AND dst.op_depth = ?5) " \ - "FROM nodes " \ - "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ - "" - -#define STMT_SELECT_OP_DEPTH_CHILDREN 31 -#define STMT_31_INFO {"STMT_SELECT_OP_DEPTH_CHILDREN", NULL} -#define STMT_31 \ + " s.wc_id, ?4 , ?5 , ?6 , " \ + " s.repos_id, " \ + " s.repos_path, s.revision, s.presence, s.depth, s.kind, s.changed_revision, " \ + " s.changed_date, s.changed_author, s.checksum, s.properties, " \ + " CASE WHEN d.checksum=s.checksum THEN d.translated_size END, " \ + " CASE WHEN d.checksum=s.checksum THEN d.last_mod_time END, " \ + " s.symlink_target, 1, d.moved_to " \ + "FROM nodes s " \ + "LEFT JOIN nodes d ON d.wc_id=?1 AND d.local_relpath=?4 AND d.op_depth=?5 " \ + "WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3 " \ + "" + +#define STMT_SELECT_NO_LONGER_MOVED_RV 32 +#define STMT_32_INFO {"STMT_SELECT_NO_LONGER_MOVED_RV", NULL} +#define STMT_32 \ + "SELECT d.local_relpath, (CASE WHEN (?2) = '' THEN (CASE WHEN (?4) = '' THEN (d.local_relpath) WHEN (d.local_relpath) = '' THEN (?4) ELSE (?4) || '/' || (d.local_relpath) END) WHEN (?4) = '' THEN (CASE WHEN (?2) = '' THEN (d.local_relpath) WHEN SUBSTR((d.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(d.local_relpath) THEN '' WHEN SUBSTR((d.local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((d.local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((d.local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(d.local_relpath) THEN (?4) WHEN SUBSTR((d.local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?4) || SUBSTR((d.local_relpath), LENGTH(?2)+1) END END) srp, " \ + " b.presence, b.op_depth " \ + "FROM nodes d " \ + "LEFT OUTER JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = d.local_relpath " \ + " AND b.op_depth = (SELECT MAX(x.op_depth) FROM nodes x " \ + " WHERE x.wc_id = ?1 " \ + " AND x.local_relpath = b.local_relpath " \ + " AND x.op_depth < ?3) " \ + "WHERE d.wc_id = ?1 " \ + " AND (((d.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((d.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + " AND d.op_depth = ?3 " \ + " AND NOT EXISTS(SELECT * FROM nodes s " \ + " WHERE s.wc_id = ?1 " \ + " AND s.local_relpath = srp " \ + " AND s.op_depth = ?5) " \ + "ORDER BY d.local_relpath DESC " \ + "" + +#define STMT_SELECT_OP_DEPTH_CHILDREN 33 +#define STMT_33_INFO {"STMT_SELECT_OP_DEPTH_CHILDREN", NULL} +#define STMT_33 \ "SELECT local_relpath, kind FROM nodes " \ "WHERE wc_id = ?1 " \ " AND parent_relpath = ?2 " \ " AND op_depth = ?3 " \ " AND presence != 'base-deleted' " \ " AND file_external is NULL " \ + "ORDER BY local_relpath " \ "" -#define STMT_SELECT_GE_OP_DEPTH_CHILDREN 32 -#define STMT_32_INFO {"STMT_SELECT_GE_OP_DEPTH_CHILDREN", NULL} -#define STMT_32 \ +#define STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS 34 +#define STMT_34_INFO {"STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS", NULL} +#define STMT_34 \ + "SELECT local_relpath, kind FROM nodes " \ + "WHERE wc_id = ?1 " \ + " AND parent_relpath = ?2 " \ + " AND op_depth = ?3 " \ + " AND presence IN ('normal', 'incomplete') " \ + "ORDER BY local_relpath " \ + "" + +#define STMT_SELECT_GE_OP_DEPTH_CHILDREN 35 +#define STMT_35_INFO {"STMT_SELECT_GE_OP_DEPTH_CHILDREN", NULL} +#define STMT_35 \ "SELECT 1 FROM nodes " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ - " AND (op_depth > ?3 OR (op_depth = ?3 AND presence != 'base-deleted')) " \ + " AND (op_depth > ?3 OR (op_depth = ?3 " \ + " AND presence IN ('normal', 'incomplete'))) " \ "UNION ALL " \ "SELECT 1 FROM ACTUAL_NODE a " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ @@ -354,9 +414,9 @@ " WHERE wc_id = ?1 AND n.local_relpath = a.local_relpath) " \ "" -#define STMT_DELETE_SHADOWED_RECURSIVE 33 -#define STMT_33_INFO {"STMT_DELETE_SHADOWED_RECURSIVE", NULL} -#define STMT_33 \ +#define STMT_DELETE_SHADOWED_RECURSIVE 36 +#define STMT_36_INFO {"STMT_DELETE_SHADOWED_RECURSIVE", NULL} +#define STMT_36 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ @@ -364,32 +424,33 @@ " OR (op_depth = ?3 AND presence = 'base-deleted')) " \ "" -#define STMT_CLEAR_MOVED_TO_FROM_DEST 34 -#define STMT_34_INFO {"STMT_CLEAR_MOVED_TO_FROM_DEST", NULL} -#define STMT_34 \ +#define STMT_CLEAR_MOVED_TO_FROM_DEST 37 +#define STMT_37_INFO {"STMT_CLEAR_MOVED_TO_FROM_DEST", NULL} +#define STMT_37 \ "UPDATE NODES SET moved_to = NULL " \ "WHERE wc_id = ?1 " \ " AND moved_to = ?2 " \ "" -#define STMT_SELECT_NOT_PRESENT_DESCENDANTS 35 -#define STMT_35_INFO {"STMT_SELECT_NOT_PRESENT_DESCENDANTS", NULL} -#define STMT_35 \ +#define STMT_SELECT_NOT_PRESENT_DESCENDANTS 38 +#define STMT_38_INFO {"STMT_SELECT_NOT_PRESENT_DESCENDANTS", NULL} +#define STMT_38 \ "SELECT local_relpath FROM nodes " \ "WHERE wc_id = ?1 AND op_depth = ?3 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ " AND presence = 'not-present' " \ "" -#define STMT_COMMIT_DESCENDANTS_TO_BASE 36 -#define STMT_36_INFO {"STMT_COMMIT_DESCENDANTS_TO_BASE", NULL} -#define STMT_36 \ +#define STMT_COMMIT_DESCENDANTS_TO_BASE 39 +#define STMT_39_INFO {"STMT_COMMIT_DESCENDANTS_TO_BASE", NULL} +#define STMT_39 \ "UPDATE NODES SET op_depth = 0, " \ " repos_id = ?4, " \ - " repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1), " \ + " repos_path = (CASE WHEN (?2) = '' THEN (CASE WHEN (?5) = '' THEN (local_relpath) WHEN (local_relpath) = '' THEN (?5) ELSE (?5) || '/' || (local_relpath) END) WHEN (?5) = '' THEN (CASE WHEN (?2) = '' THEN (local_relpath) WHEN SUBSTR((local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(local_relpath) THEN '' WHEN SUBSTR((local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(local_relpath) THEN (?5) WHEN SUBSTR((local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?5) || SUBSTR((local_relpath), LENGTH(?2)+1) END END), " \ " revision = ?6, " \ " dav_cache = NULL, " \ " moved_here = NULL, " \ + " moved_to = NULL, " \ " presence = CASE presence " \ " WHEN 'normal' THEN 'normal' " \ " WHEN 'excluded' THEN 'excluded' " \ @@ -400,67 +461,78 @@ " AND op_depth = ?3 " \ "" -#define STMT_SELECT_NODE_CHILDREN 37 -#define STMT_37_INFO {"STMT_SELECT_NODE_CHILDREN", NULL} -#define STMT_37 \ - "SELECT local_relpath FROM nodes " \ +#define STMT_SELECT_NODE_CHILDREN 40 +#define STMT_40_INFO {"STMT_SELECT_NODE_CHILDREN", NULL} +#define STMT_40 \ + "SELECT DISTINCT local_relpath FROM nodes " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ + "ORDER BY local_relpath " \ "" -#define STMT_SELECT_WORKING_CHILDREN 38 -#define STMT_38_INFO {"STMT_SELECT_WORKING_CHILDREN", NULL} -#define STMT_38 \ - "SELECT local_relpath FROM nodes " \ +#define STMT_SELECT_WORKING_CHILDREN 41 +#define STMT_41_INFO {"STMT_SELECT_WORKING_CHILDREN", NULL} +#define STMT_41 \ + "SELECT DISTINCT local_relpath FROM nodes " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ " AND (op_depth > (SELECT MAX(op_depth) FROM nodes " \ " WHERE wc_id = ?1 AND local_relpath = ?2) " \ " OR " \ " (op_depth = (SELECT MAX(op_depth) FROM nodes " \ " WHERE wc_id = ?1 AND local_relpath = ?2) " \ - " AND presence != 'base-deleted')) " \ + " AND presence IN ('normal', 'incomplete'))) " \ + "ORDER BY local_relpath " \ "" -#define STMT_SELECT_NODE_PROPS 39 -#define STMT_39_INFO {"STMT_SELECT_NODE_PROPS", NULL} -#define STMT_39 \ +#define STMT_SELECT_BASE_NOT_PRESENT_CHILDREN 42 +#define STMT_42_INFO {"STMT_SELECT_BASE_NOT_PRESENT_CHILDREN", NULL} +#define STMT_42 \ + "SELECT local_relpath FROM nodes " \ + "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 " \ + " AND presence = 'not-present' " \ + "ORDER BY local_relpath " \ + "" + +#define STMT_SELECT_NODE_PROPS 43 +#define STMT_43_INFO {"STMT_SELECT_NODE_PROPS", NULL} +#define STMT_43 \ "SELECT properties, presence FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "ORDER BY op_depth DESC " \ "" -#define STMT_SELECT_ACTUAL_PROPS 40 -#define STMT_40_INFO {"STMT_SELECT_ACTUAL_PROPS", NULL} -#define STMT_40 \ +#define STMT_SELECT_ACTUAL_PROPS 44 +#define STMT_44_INFO {"STMT_SELECT_ACTUAL_PROPS", NULL} +#define STMT_44 \ "SELECT properties FROM actual_node " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_UPDATE_ACTUAL_PROPS 41 -#define STMT_41_INFO {"STMT_UPDATE_ACTUAL_PROPS", NULL} -#define STMT_41 \ +#define STMT_UPDATE_ACTUAL_PROPS 45 +#define STMT_45_INFO {"STMT_UPDATE_ACTUAL_PROPS", NULL} +#define STMT_45 \ "UPDATE actual_node SET properties = ?3 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_INSERT_ACTUAL_PROPS 42 -#define STMT_42_INFO {"STMT_INSERT_ACTUAL_PROPS", NULL} -#define STMT_42 \ +#define STMT_INSERT_ACTUAL_PROPS 46 +#define STMT_46_INFO {"STMT_INSERT_ACTUAL_PROPS", NULL} +#define STMT_46 \ "INSERT INTO actual_node (wc_id, local_relpath, parent_relpath, properties) " \ "VALUES (?1, ?2, ?3, ?4) " \ "" -#define STMT_INSERT_LOCK 43 -#define STMT_43_INFO {"STMT_INSERT_LOCK", NULL} -#define STMT_43 \ +#define STMT_INSERT_LOCK 47 +#define STMT_47_INFO {"STMT_INSERT_LOCK", NULL} +#define STMT_47 \ "INSERT OR REPLACE INTO lock " \ "(repos_id, repos_relpath, lock_token, lock_owner, lock_comment, " \ " lock_date) " \ "VALUES (?1, ?2, ?3, ?4, ?5, ?6) " \ "" -#define STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE 44 -#define STMT_44_INFO {"STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE", NULL} -#define STMT_44 \ +#define STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE 48 +#define STMT_48_INFO {"STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE", NULL} +#define STMT_48 \ "SELECT nodes.repos_id, nodes.repos_path, lock_token " \ "FROM nodes " \ "LEFT JOIN lock ON nodes.repos_id = lock.repos_id " \ @@ -469,93 +541,73 @@ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_INSERT_WCROOT 45 -#define STMT_45_INFO {"STMT_INSERT_WCROOT", NULL} -#define STMT_45 \ +#define STMT_INSERT_WCROOT 49 +#define STMT_49_INFO {"STMT_INSERT_WCROOT", NULL} +#define STMT_49 \ "INSERT INTO wcroot (local_abspath) " \ "VALUES (?1) " \ "" -#define STMT_UPDATE_BASE_NODE_DAV_CACHE 46 -#define STMT_46_INFO {"STMT_UPDATE_BASE_NODE_DAV_CACHE", NULL} -#define STMT_46 \ +#define STMT_UPDATE_BASE_NODE_DAV_CACHE 50 +#define STMT_50_INFO {"STMT_UPDATE_BASE_NODE_DAV_CACHE", NULL} +#define STMT_50 \ "UPDATE nodes SET dav_cache = ?3 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_SELECT_BASE_DAV_CACHE 47 -#define STMT_47_INFO {"STMT_SELECT_BASE_DAV_CACHE", NULL} -#define STMT_47 \ +#define STMT_SELECT_BASE_DAV_CACHE 51 +#define STMT_51_INFO {"STMT_SELECT_BASE_DAV_CACHE", NULL} +#define STMT_51 \ "SELECT dav_cache FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_SELECT_DELETION_INFO 48 -#define STMT_48_INFO {"STMT_SELECT_DELETION_INFO", NULL} -#define STMT_48 \ - "SELECT (SELECT b.presence FROM nodes AS b " \ - " WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0), " \ - " work.presence, work.op_depth " \ - "FROM nodes_current AS work " \ - "WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0 " \ - "LIMIT 1 " \ - "" - -#define STMT_SELECT_DELETION_INFO_SCAN 49 -#define STMT_49_INFO {"STMT_SELECT_DELETION_INFO_SCAN", NULL} -#define STMT_49 \ - "SELECT (SELECT b.presence FROM nodes AS b " \ - " WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0), " \ - " work.presence, work.op_depth, moved.moved_to " \ - "FROM nodes_current AS work " \ - "LEFT OUTER JOIN nodes AS moved " \ - " ON moved.wc_id = work.wc_id " \ - " AND moved.local_relpath = work.local_relpath " \ - " AND moved.moved_to IS NOT NULL " \ - "WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0 " \ +#define STMT_SELECT_DELETION_INFO 52 +#define STMT_52_INFO {"STMT_SELECT_DELETION_INFO", NULL} +#define STMT_52 \ + "SELECT b.presence, w.presence, w.op_depth, w.moved_to " \ + "FROM nodes w " \ + "LEFT JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0 " \ + "WHERE w.wc_id = ?1 AND w.local_relpath = ?2 " \ + " AND w.op_depth = (SELECT MAX(op_depth) FROM nodes d " \ + " WHERE d.wc_id = ?1 AND d.local_relpath = ?2 " \ + " AND d.op_depth > 0) " \ "LIMIT 1 " \ "" -#define STMT_SELECT_MOVED_TO_NODE 50 -#define STMT_50_INFO {"STMT_SELECT_MOVED_TO_NODE", NULL} -#define STMT_50 \ +#define STMT_SELECT_MOVED_TO_NODE 53 +#define STMT_53_INFO {"STMT_SELECT_MOVED_TO_NODE", NULL} +#define STMT_53 \ "SELECT op_depth, moved_to " \ "FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND moved_to IS NOT NULL " \ "ORDER BY op_depth DESC " \ "" -#define STMT_SELECT_OP_DEPTH_MOVED_TO 51 -#define STMT_51_INFO {"STMT_SELECT_OP_DEPTH_MOVED_TO", NULL} -#define STMT_51 \ - "SELECT op_depth, moved_to, repos_path, revision " \ +#define STMT_SELECT_OP_DEPTH_MOVED_TO 54 +#define STMT_54_INFO {"STMT_SELECT_OP_DEPTH_MOVED_TO", NULL} +#define STMT_54 \ + "SELECT op_depth, moved_to " \ "FROM nodes " \ - "WHERE wc_id = ?1 AND local_relpath = ?2 " \ - " AND op_depth <= (SELECT MIN(op_depth) FROM nodes " \ - " WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3) " \ - "ORDER BY op_depth DESC " \ + "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 " \ + " AND EXISTS(SELECT * from nodes " \ + " WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ + " AND presence IN ('normal', 'incomplete')) " \ + "ORDER BY op_depth ASC " \ + "LIMIT 1 " \ "" -#define STMT_SELECT_MOVED_TO 52 -#define STMT_52_INFO {"STMT_SELECT_MOVED_TO", NULL} -#define STMT_52 \ +#define STMT_SELECT_MOVED_TO 55 +#define STMT_55_INFO {"STMT_SELECT_MOVED_TO", NULL} +#define STMT_55 \ "SELECT moved_to " \ "FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ "" -#define STMT_SELECT_MOVED_HERE 53 -#define STMT_53_INFO {"STMT_SELECT_MOVED_HERE", NULL} -#define STMT_53 \ - "SELECT moved_here, presence, repos_path, revision " \ - "FROM nodes " \ - "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth >= ?3 " \ - "ORDER BY op_depth " \ - "" - -#define STMT_SELECT_MOVED_BACK 54 -#define STMT_54_INFO {"STMT_SELECT_MOVED_BACK", NULL} -#define STMT_54 \ +#define STMT_SELECT_MOVED_BACK 56 +#define STMT_56_INFO {"STMT_SELECT_MOVED_BACK", NULL} +#define STMT_56 \ "SELECT u.local_relpath, " \ " u.presence, u.repos_id, u.repos_path, u.revision, " \ " l.presence, l.repos_id, l.repos_path, l.revision, " \ @@ -581,81 +633,71 @@ " AND u.op_depth = ?4 " \ "" -#define STMT_DELETE_MOVED_BACK 55 -#define STMT_55_INFO {"STMT_DELETE_MOVED_BACK", NULL} -#define STMT_55 \ - "DELETE FROM nodes " \ - "WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 " \ - " OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ - " AND op_depth = ?3 " \ - "" - -#define STMT_DELETE_LOCK 56 -#define STMT_56_INFO {"STMT_DELETE_LOCK", NULL} -#define STMT_56 \ +#define STMT_DELETE_LOCK 57 +#define STMT_57_INFO {"STMT_DELETE_LOCK", NULL} +#define STMT_57 \ "DELETE FROM lock " \ "WHERE repos_id = ?1 AND repos_relpath = ?2 " \ "" -#define STMT_DELETE_LOCK_RECURSIVELY 57 -#define STMT_57_INFO {"STMT_DELETE_LOCK_RECURSIVELY", NULL} -#define STMT_57 \ +#define STMT_DELETE_LOCK_RECURSIVELY 58 +#define STMT_58_INFO {"STMT_DELETE_LOCK_RECURSIVELY", NULL} +#define STMT_58 \ "DELETE FROM lock " \ "WHERE repos_id = ?1 AND (repos_relpath = ?2 OR (((repos_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((repos_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ "" -#define STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE 58 -#define STMT_58_INFO {"STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE", NULL} -#define STMT_58 \ +#define STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE 59 +#define STMT_59_INFO {"STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE", NULL} +#define STMT_59 \ "UPDATE nodes SET dav_cache = NULL " \ "WHERE dav_cache IS NOT NULL AND wc_id = ?1 AND op_depth = 0 " \ " AND (local_relpath = ?2 " \ " OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ "" -#define STMT_RECURSIVE_UPDATE_NODE_REPO 59 -#define STMT_59_INFO {"STMT_RECURSIVE_UPDATE_NODE_REPO", NULL} -#define STMT_59 \ +#define STMT_RECURSIVE_UPDATE_NODE_REPO 60 +#define STMT_60_INFO {"STMT_RECURSIVE_UPDATE_NODE_REPO", NULL} +#define STMT_60 \ "UPDATE nodes SET repos_id = ?4, dav_cache = NULL " \ "WHERE (wc_id = ?1 AND local_relpath = ?2 AND repos_id = ?3) " \ " OR (wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ " AND repos_id = ?3) " \ "" -#define STMT_UPDATE_LOCK_REPOS_ID 60 -#define STMT_60_INFO {"STMT_UPDATE_LOCK_REPOS_ID", NULL} -#define STMT_60 \ +#define STMT_UPDATE_LOCK_REPOS_ID 61 +#define STMT_61_INFO {"STMT_UPDATE_LOCK_REPOS_ID", NULL} +#define STMT_61 \ "UPDATE lock SET repos_id = ?2 " \ "WHERE repos_id = ?1 " \ "" -#define STMT_UPDATE_NODE_FILEINFO 61 -#define STMT_61_INFO {"STMT_UPDATE_NODE_FILEINFO", NULL} -#define STMT_61 \ +#define STMT_UPDATE_NODE_FILEINFO 62 +#define STMT_62_INFO {"STMT_UPDATE_NODE_FILEINFO", NULL} +#define STMT_62 \ "UPDATE nodes SET translated_size = ?3, last_mod_time = ?4 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ " AND op_depth = (SELECT MAX(op_depth) FROM nodes " \ " WHERE wc_id = ?1 AND local_relpath = ?2) " \ "" -#define STMT_INSERT_ACTUAL_CONFLICT 62 -#define STMT_62_INFO {"STMT_INSERT_ACTUAL_CONFLICT", NULL} -#define STMT_62 \ +#define STMT_INSERT_ACTUAL_CONFLICT 63 +#define STMT_63_INFO {"STMT_INSERT_ACTUAL_CONFLICT", NULL} +#define STMT_63 \ "INSERT INTO actual_node (wc_id, local_relpath, conflict_data, parent_relpath) " \ "VALUES (?1, ?2, ?3, ?4) " \ "" -#define STMT_UPDATE_ACTUAL_CONFLICT 63 -#define STMT_63_INFO {"STMT_UPDATE_ACTUAL_CONFLICT", NULL} -#define STMT_63 \ +#define STMT_UPDATE_ACTUAL_CONFLICT 64 +#define STMT_64_INFO {"STMT_UPDATE_ACTUAL_CONFLICT", NULL} +#define STMT_64 \ "UPDATE actual_node SET conflict_data = ?3 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_UPDATE_ACTUAL_CHANGELISTS 64 -#define STMT_64_INFO {"STMT_UPDATE_ACTUAL_CHANGELISTS", NULL} -#define STMT_64 \ +#define STMT_UPDATE_ACTUAL_CHANGELISTS 65 +#define STMT_65_INFO {"STMT_UPDATE_ACTUAL_CHANGELISTS", NULL} +#define STMT_65 \ "UPDATE actual_node SET changelist = ?3 " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ @@ -665,16 +707,16 @@ " AND kind = 'file') " \ "" -#define STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST 65 -#define STMT_65_INFO {"STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST", NULL} -#define STMT_65 \ +#define STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST 66 +#define STMT_66_INFO {"STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST", NULL} +#define STMT_66 \ "UPDATE actual_node SET changelist = NULL " \ " WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_MARK_SKIPPED_CHANGELIST_DIRS 66 -#define STMT_66_INFO {"STMT_MARK_SKIPPED_CHANGELIST_DIRS", NULL} -#define STMT_66 \ +#define STMT_MARK_SKIPPED_CHANGELIST_DIRS 67 +#define STMT_67_INFO {"STMT_MARK_SKIPPED_CHANGELIST_DIRS", NULL} +#define STMT_67 \ "INSERT INTO changelist_list (wc_id, local_relpath, notify, changelist) " \ "SELECT wc_id, local_relpath, 7, ?3 " \ "FROM targets_list " \ @@ -683,17 +725,17 @@ " AND kind = 'dir' " \ "" -#define STMT_RESET_ACTUAL_WITH_CHANGELIST 67 -#define STMT_67_INFO {"STMT_RESET_ACTUAL_WITH_CHANGELIST", NULL} -#define STMT_67 \ +#define STMT_RESET_ACTUAL_WITH_CHANGELIST 68 +#define STMT_68_INFO {"STMT_RESET_ACTUAL_WITH_CHANGELIST", NULL} +#define STMT_68 \ "REPLACE INTO actual_node ( " \ " wc_id, local_relpath, parent_relpath, changelist) " \ "VALUES (?1, ?2, ?3, ?4) " \ "" -#define STMT_CREATE_CHANGELIST_LIST 68 -#define STMT_68_INFO {"STMT_CREATE_CHANGELIST_LIST", NULL} -#define STMT_68 \ +#define STMT_CREATE_CHANGELIST_LIST 69 +#define STMT_69_INFO {"STMT_CREATE_CHANGELIST_LIST", NULL} +#define STMT_69 \ "DROP TABLE IF EXISTS changelist_list; " \ "CREATE TEMPORARY TABLE changelist_list ( " \ " wc_id INTEGER NOT NULL, " \ @@ -704,9 +746,9 @@ ") " \ "" -#define STMT_CREATE_CHANGELIST_TRIGGER 69 -#define STMT_69_INFO {"STMT_CREATE_CHANGELIST_TRIGGER", NULL} -#define STMT_69 \ +#define STMT_CREATE_CHANGELIST_TRIGGER 70 +#define STMT_70_INFO {"STMT_CREATE_CHANGELIST_TRIGGER", NULL} +#define STMT_70 \ "DROP TRIGGER IF EXISTS trigger_changelist_list_change; " \ "CREATE TEMPORARY TRIGGER trigger_changelist_list_change " \ "BEFORE UPDATE ON actual_node " \ @@ -721,25 +763,25 @@ "END " \ "" -#define STMT_FINALIZE_CHANGELIST 70 -#define STMT_70_INFO {"STMT_FINALIZE_CHANGELIST", NULL} -#define STMT_70 \ +#define STMT_FINALIZE_CHANGELIST 71 +#define STMT_71_INFO {"STMT_FINALIZE_CHANGELIST", NULL} +#define STMT_71 \ "DROP TRIGGER trigger_changelist_list_change; " \ "DROP TABLE changelist_list; " \ "DROP TABLE targets_list " \ "" -#define STMT_SELECT_CHANGELIST_LIST 71 -#define STMT_71_INFO {"STMT_SELECT_CHANGELIST_LIST", NULL} -#define STMT_71 \ +#define STMT_SELECT_CHANGELIST_LIST 72 +#define STMT_72_INFO {"STMT_SELECT_CHANGELIST_LIST", NULL} +#define STMT_72 \ "SELECT wc_id, local_relpath, notify, changelist " \ "FROM changelist_list " \ "ORDER BY wc_id, local_relpath ASC, notify DESC " \ "" -#define STMT_CREATE_TARGETS_LIST 72 -#define STMT_72_INFO {"STMT_CREATE_TARGETS_LIST", NULL} -#define STMT_72 \ +#define STMT_CREATE_TARGETS_LIST 73 +#define STMT_73_INFO {"STMT_CREATE_TARGETS_LIST", NULL} +#define STMT_73 \ "DROP TABLE IF EXISTS targets_list; " \ "CREATE TEMPORARY TABLE targets_list ( " \ " wc_id INTEGER NOT NULL, " \ @@ -750,15 +792,15 @@ " ); " \ "" -#define STMT_DROP_TARGETS_LIST 73 -#define STMT_73_INFO {"STMT_DROP_TARGETS_LIST", NULL} -#define STMT_73 \ +#define STMT_DROP_TARGETS_LIST 74 +#define STMT_74_INFO {"STMT_DROP_TARGETS_LIST", NULL} +#define STMT_74 \ "DROP TABLE targets_list " \ "" -#define STMT_INSERT_TARGET 74 -#define STMT_74_INFO {"STMT_INSERT_TARGET", NULL} -#define STMT_74 \ +#define STMT_INSERT_TARGET 75 +#define STMT_75_INFO {"STMT_INSERT_TARGET", NULL} +#define STMT_75 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT wc_id, local_relpath, parent_relpath, kind " \ "FROM nodes_current " \ @@ -766,9 +808,9 @@ " AND local_relpath = ?2 " \ "" -#define STMT_INSERT_TARGET_DEPTH_FILES 75 -#define STMT_75_INFO {"STMT_INSERT_TARGET_DEPTH_FILES", NULL} -#define STMT_75 \ +#define STMT_INSERT_TARGET_DEPTH_FILES 76 +#define STMT_76_INFO {"STMT_INSERT_TARGET_DEPTH_FILES", NULL} +#define STMT_76 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT wc_id, local_relpath, parent_relpath, kind " \ "FROM nodes_current " \ @@ -777,9 +819,9 @@ " AND kind = 'file' " \ "" -#define STMT_INSERT_TARGET_DEPTH_IMMEDIATES 76 -#define STMT_76_INFO {"STMT_INSERT_TARGET_DEPTH_IMMEDIATES", NULL} -#define STMT_76 \ +#define STMT_INSERT_TARGET_DEPTH_IMMEDIATES 77 +#define STMT_77_INFO {"STMT_INSERT_TARGET_DEPTH_IMMEDIATES", NULL} +#define STMT_77 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT wc_id, local_relpath, parent_relpath, kind " \ "FROM nodes_current " \ @@ -787,9 +829,9 @@ " AND parent_relpath = ?2 " \ "" -#define STMT_INSERT_TARGET_DEPTH_INFINITY 77 -#define STMT_77_INFO {"STMT_INSERT_TARGET_DEPTH_INFINITY", NULL} -#define STMT_77 \ +#define STMT_INSERT_TARGET_DEPTH_INFINITY 78 +#define STMT_78_INFO {"STMT_INSERT_TARGET_DEPTH_INFINITY", NULL} +#define STMT_78 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT wc_id, local_relpath, parent_relpath, kind " \ "FROM nodes_current " \ @@ -797,9 +839,9 @@ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_INSERT_TARGET_WITH_CHANGELIST 78 -#define STMT_78_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST", NULL} -#define STMT_78 \ +#define STMT_INSERT_TARGET_WITH_CHANGELIST 79 +#define STMT_79_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST", NULL} +#define STMT_79 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind " \ " FROM actual_node AS A JOIN nodes_current AS N " \ @@ -809,9 +851,9 @@ " AND A.changelist = ?3 " \ "" -#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES 79 -#define STMT_79_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES", NULL} -#define STMT_79 \ +#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES 80 +#define STMT_80_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES", NULL} +#define STMT_80 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind " \ " FROM actual_node AS A JOIN nodes_current AS N " \ @@ -822,9 +864,9 @@ " AND A.changelist = ?3 " \ "" -#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES 80 -#define STMT_80_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES", NULL} -#define STMT_80 \ +#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES 81 +#define STMT_81_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES", NULL} +#define STMT_81 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind " \ " FROM actual_node AS A JOIN nodes_current AS N " \ @@ -834,9 +876,9 @@ " AND A.changelist = ?3 " \ "" -#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY 81 -#define STMT_81_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY", NULL} -#define STMT_81 \ +#define STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY 82 +#define STMT_82_INFO {"STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY", NULL} +#define STMT_82 \ "INSERT INTO targets_list(wc_id, local_relpath, parent_relpath, kind) " \ "SELECT N.wc_id, N.local_relpath, N.parent_relpath, N.kind " \ " FROM actual_node AS A JOIN nodes_current AS N " \ @@ -846,18 +888,28 @@ " AND A.changelist = ?3 " \ "" -#define STMT_INSERT_ACTUAL_EMPTIES 82 -#define STMT_82_INFO {"STMT_INSERT_ACTUAL_EMPTIES", NULL} -#define STMT_82 \ +#define STMT_INSERT_ACTUAL_EMPTIES 83 +#define STMT_83_INFO {"STMT_INSERT_ACTUAL_EMPTIES", NULL} +#define STMT_83 \ "INSERT OR IGNORE INTO actual_node ( " \ " wc_id, local_relpath, parent_relpath) " \ "SELECT wc_id, local_relpath, parent_relpath " \ "FROM targets_list " \ "" -#define STMT_DELETE_ACTUAL_EMPTY 83 -#define STMT_83_INFO {"STMT_DELETE_ACTUAL_EMPTY", NULL} -#define STMT_83 \ +#define STMT_INSERT_ACTUAL_EMPTIES_FILES 84 +#define STMT_84_INFO {"STMT_INSERT_ACTUAL_EMPTIES_FILES", NULL} +#define STMT_84 \ + "INSERT OR IGNORE INTO actual_node ( " \ + " wc_id, local_relpath, parent_relpath) " \ + "SELECT wc_id, local_relpath, parent_relpath " \ + "FROM targets_list " \ + "WHERE kind='file' " \ + "" + +#define STMT_DELETE_ACTUAL_EMPTY 85 +#define STMT_85_INFO {"STMT_DELETE_ACTUAL_EMPTY", NULL} +#define STMT_85 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ " AND properties IS NULL " \ @@ -869,12 +921,12 @@ " AND left_checksum IS NULL " \ "" -#define STMT_DELETE_ACTUAL_EMPTIES 84 -#define STMT_84_INFO {"STMT_DELETE_ACTUAL_EMPTIES", NULL} -#define STMT_84 \ +#define STMT_DELETE_ACTUAL_EMPTIES 86 +#define STMT_86_INFO {"STMT_DELETE_ACTUAL_EMPTIES", NULL} +#define STMT_86 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 " \ - " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ " AND properties IS NULL " \ " AND conflict_data IS NULL " \ " AND changelist IS NULL " \ @@ -884,25 +936,25 @@ " AND left_checksum IS NULL " \ "" -#define STMT_DELETE_BASE_NODE 85 -#define STMT_85_INFO {"STMT_DELETE_BASE_NODE", NULL} -#define STMT_85 \ +#define STMT_DELETE_BASE_NODE 87 +#define STMT_87_INFO {"STMT_DELETE_BASE_NODE", NULL} +#define STMT_87 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_DELETE_WORKING_NODE 86 -#define STMT_86_INFO {"STMT_DELETE_WORKING_NODE", NULL} -#define STMT_86 \ +#define STMT_DELETE_WORKING_NODE 88 +#define STMT_88_INFO {"STMT_DELETE_WORKING_NODE", NULL} +#define STMT_88 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ " AND op_depth = (SELECT MAX(op_depth) FROM nodes " \ " WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > 0) " \ "" -#define STMT_DELETE_LOWEST_WORKING_NODE 87 -#define STMT_87_INFO {"STMT_DELETE_LOWEST_WORKING_NODE", NULL} -#define STMT_87 \ +#define STMT_DELETE_LOWEST_WORKING_NODE 89 +#define STMT_89_INFO {"STMT_DELETE_LOWEST_WORKING_NODE", NULL} +#define STMT_89 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ " AND op_depth = (SELECT MIN(op_depth) FROM nodes " \ @@ -910,16 +962,16 @@ " AND presence = 'base-deleted' " \ "" -#define STMT_DELETE_NODE_ALL_LAYERS 88 -#define STMT_88_INFO {"STMT_DELETE_NODE_ALL_LAYERS", NULL} -#define STMT_88 \ +#define STMT_DELETE_NODE_ALL_LAYERS 90 +#define STMT_90_INFO {"STMT_DELETE_NODE_ALL_LAYERS", NULL} +#define STMT_90 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE 89 -#define STMT_89_INFO {"STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE", NULL} -#define STMT_89 \ +#define STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE 91 +#define STMT_91_INFO {"STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE", NULL} +#define STMT_91 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -927,25 +979,25 @@ " AND op_depth >= ?3 " \ "" -#define STMT_DELETE_ACTUAL_NODE 90 -#define STMT_90_INFO {"STMT_DELETE_ACTUAL_NODE", NULL} -#define STMT_90 \ +#define STMT_DELETE_ACTUAL_NODE 92 +#define STMT_92_INFO {"STMT_DELETE_ACTUAL_NODE", NULL} +#define STMT_92 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_DELETE_ACTUAL_NODE_RECURSIVE 91 -#define STMT_91_INFO {"STMT_DELETE_ACTUAL_NODE_RECURSIVE", NULL} -#define STMT_91 \ +#define STMT_DELETE_ACTUAL_NODE_RECURSIVE 93 +#define STMT_93_INFO {"STMT_DELETE_ACTUAL_NODE_RECURSIVE", NULL} +#define STMT_93 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ " OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ "" -#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST 92 -#define STMT_92_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST", NULL} -#define STMT_92 \ +#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST 94 +#define STMT_94_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST", NULL} +#define STMT_94 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 " \ " AND local_relpath = ?2 " \ @@ -955,9 +1007,9 @@ " AND c.kind = 'file')) " \ "" -#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE 93 -#define STMT_93_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE", NULL} -#define STMT_93 \ +#define STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE 95 +#define STMT_95_INFO {"STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE", NULL} +#define STMT_95 \ "DELETE FROM actual_node " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -969,9 +1021,9 @@ " AND c.kind = 'file')) " \ "" -#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST 94 -#define STMT_94_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST", NULL} -#define STMT_94 \ +#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST 96 +#define STMT_96_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST", NULL} +#define STMT_96 \ "UPDATE actual_node " \ "SET properties = NULL, " \ " text_mod = NULL, " \ @@ -983,9 +1035,23 @@ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE 95 -#define STMT_95_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE", NULL} -#define STMT_95 \ +#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT 97 +#define STMT_97_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT", NULL} +#define STMT_97 \ + "UPDATE actual_node " \ + "SET properties = NULL, " \ + " text_mod = NULL, " \ + " tree_conflict_data = NULL, " \ + " older_checksum = NULL, " \ + " left_checksum = NULL, " \ + " right_checksum = NULL, " \ + " changelist = NULL " \ + "WHERE wc_id = ?1 AND local_relpath = ?2 " \ + "" + +#define STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE 98 +#define STMT_98_INFO {"STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE", NULL} +#define STMT_98 \ "UPDATE actual_node " \ "SET properties = NULL, " \ " text_mod = NULL, " \ @@ -999,108 +1065,109 @@ " OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ "" -#define STMT_UPDATE_NODE_BASE_DEPTH 96 -#define STMT_96_INFO {"STMT_UPDATE_NODE_BASE_DEPTH", NULL} -#define STMT_96 \ +#define STMT_UPDATE_NODE_BASE_DEPTH 99 +#define STMT_99_INFO {"STMT_UPDATE_NODE_BASE_DEPTH", NULL} +#define STMT_99 \ "UPDATE nodes SET depth = ?3 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ " AND kind='dir' " \ + " AND presence IN ('normal', 'incomplete') " \ "" -#define STMT_UPDATE_NODE_BASE_PRESENCE 97 -#define STMT_97_INFO {"STMT_UPDATE_NODE_BASE_PRESENCE", NULL} -#define STMT_97 \ +#define STMT_UPDATE_NODE_BASE_PRESENCE 100 +#define STMT_100_INFO {"STMT_UPDATE_NODE_BASE_PRESENCE", NULL} +#define STMT_100 \ "UPDATE nodes SET presence = ?3 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH 98 -#define STMT_98_INFO {"STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH", NULL} -#define STMT_98 \ +#define STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH 101 +#define STMT_101_INFO {"STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH", NULL} +#define STMT_101 \ "UPDATE nodes SET presence = ?3, revision = ?4, repos_path = ?5 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_LOOK_FOR_WORK 99 -#define STMT_99_INFO {"STMT_LOOK_FOR_WORK", NULL} -#define STMT_99 \ +#define STMT_LOOK_FOR_WORK 102 +#define STMT_102_INFO {"STMT_LOOK_FOR_WORK", NULL} +#define STMT_102 \ "SELECT id FROM work_queue LIMIT 1 " \ "" -#define STMT_INSERT_WORK_ITEM 100 -#define STMT_100_INFO {"STMT_INSERT_WORK_ITEM", NULL} -#define STMT_100 \ +#define STMT_INSERT_WORK_ITEM 103 +#define STMT_103_INFO {"STMT_INSERT_WORK_ITEM", NULL} +#define STMT_103 \ "INSERT INTO work_queue (work) VALUES (?1) " \ "" -#define STMT_SELECT_WORK_ITEM 101 -#define STMT_101_INFO {"STMT_SELECT_WORK_ITEM", NULL} -#define STMT_101 \ +#define STMT_SELECT_WORK_ITEM 104 +#define STMT_104_INFO {"STMT_SELECT_WORK_ITEM", NULL} +#define STMT_104 \ "SELECT id, work FROM work_queue ORDER BY id LIMIT 1 " \ "" -#define STMT_DELETE_WORK_ITEM 102 -#define STMT_102_INFO {"STMT_DELETE_WORK_ITEM", NULL} -#define STMT_102 \ +#define STMT_DELETE_WORK_ITEM 105 +#define STMT_105_INFO {"STMT_DELETE_WORK_ITEM", NULL} +#define STMT_105 \ "DELETE FROM work_queue WHERE id = ?1 " \ "" -#define STMT_INSERT_OR_IGNORE_PRISTINE 103 -#define STMT_103_INFO {"STMT_INSERT_OR_IGNORE_PRISTINE", NULL} -#define STMT_103 \ +#define STMT_INSERT_OR_IGNORE_PRISTINE 106 +#define STMT_106_INFO {"STMT_INSERT_OR_IGNORE_PRISTINE", NULL} +#define STMT_106 \ "INSERT OR IGNORE INTO pristine (checksum, md5_checksum, size, refcount) " \ "VALUES (?1, ?2, ?3, 0) " \ "" -#define STMT_INSERT_PRISTINE 104 -#define STMT_104_INFO {"STMT_INSERT_PRISTINE", NULL} -#define STMT_104 \ +#define STMT_INSERT_PRISTINE 107 +#define STMT_107_INFO {"STMT_INSERT_PRISTINE", NULL} +#define STMT_107 \ "INSERT INTO pristine (checksum, md5_checksum, size, refcount) " \ "VALUES (?1, ?2, ?3, 0) " \ "" -#define STMT_SELECT_PRISTINE 105 -#define STMT_105_INFO {"STMT_SELECT_PRISTINE", NULL} -#define STMT_105 \ +#define STMT_SELECT_PRISTINE 108 +#define STMT_108_INFO {"STMT_SELECT_PRISTINE", NULL} +#define STMT_108 \ "SELECT md5_checksum " \ "FROM pristine " \ "WHERE checksum = ?1 " \ "" -#define STMT_SELECT_PRISTINE_SIZE 106 -#define STMT_106_INFO {"STMT_SELECT_PRISTINE_SIZE", NULL} -#define STMT_106 \ +#define STMT_SELECT_PRISTINE_SIZE 109 +#define STMT_109_INFO {"STMT_SELECT_PRISTINE_SIZE", NULL} +#define STMT_109 \ "SELECT size " \ "FROM pristine " \ "WHERE checksum = ?1 LIMIT 1 " \ "" -#define STMT_SELECT_PRISTINE_BY_MD5 107 -#define STMT_107_INFO {"STMT_SELECT_PRISTINE_BY_MD5", NULL} -#define STMT_107 \ +#define STMT_SELECT_PRISTINE_BY_MD5 110 +#define STMT_110_INFO {"STMT_SELECT_PRISTINE_BY_MD5", NULL} +#define STMT_110 \ "SELECT checksum " \ "FROM pristine " \ "WHERE md5_checksum = ?1 " \ "" -#define STMT_SELECT_UNREFERENCED_PRISTINES 108 -#define STMT_108_INFO {"STMT_SELECT_UNREFERENCED_PRISTINES", NULL} -#define STMT_108 \ +#define STMT_SELECT_UNREFERENCED_PRISTINES 111 +#define STMT_111_INFO {"STMT_SELECT_UNREFERENCED_PRISTINES", NULL} +#define STMT_111 \ "SELECT checksum " \ "FROM pristine " \ "WHERE refcount = 0 " \ "" -#define STMT_DELETE_PRISTINE_IF_UNREFERENCED 109 -#define STMT_109_INFO {"STMT_DELETE_PRISTINE_IF_UNREFERENCED", NULL} -#define STMT_109 \ +#define STMT_DELETE_PRISTINE_IF_UNREFERENCED 112 +#define STMT_112_INFO {"STMT_DELETE_PRISTINE_IF_UNREFERENCED", NULL} +#define STMT_112 \ "DELETE FROM pristine " \ "WHERE checksum = ?1 AND refcount = 0 " \ "" -#define STMT_SELECT_COPY_PRISTINES 110 -#define STMT_110_INFO {"STMT_SELECT_COPY_PRISTINES", NULL} -#define STMT_110 \ +#define STMT_SELECT_COPY_PRISTINES 113 +#define STMT_113_INFO {"STMT_SELECT_COPY_PRISTINES", NULL} +#define STMT_113 \ "SELECT n.checksum, md5_checksum, size " \ "FROM nodes_current n " \ "LEFT JOIN pristine p ON n.checksum = p.checksum " \ @@ -1118,62 +1185,73 @@ " AND n.checksum IS NOT NULL " \ "" -#define STMT_VACUUM 111 -#define STMT_111_INFO {"STMT_VACUUM", NULL} -#define STMT_111 \ +#define STMT_VACUUM 114 +#define STMT_114_INFO {"STMT_VACUUM", NULL} +#define STMT_114 \ "VACUUM " \ "" -#define STMT_SELECT_CONFLICT_VICTIMS 112 -#define STMT_112_INFO {"STMT_SELECT_CONFLICT_VICTIMS", NULL} -#define STMT_112 \ +#define STMT_SELECT_CONFLICT_VICTIMS 115 +#define STMT_115_INFO {"STMT_SELECT_CONFLICT_VICTIMS", NULL} +#define STMT_115 \ "SELECT local_relpath, conflict_data " \ "FROM actual_node " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 AND " \ " NOT (conflict_data IS NULL) " \ "" -#define STMT_INSERT_WC_LOCK 113 -#define STMT_113_INFO {"STMT_INSERT_WC_LOCK", NULL} -#define STMT_113 \ +#define STMT_INSERT_WC_LOCK 116 +#define STMT_116_INFO {"STMT_INSERT_WC_LOCK", NULL} +#define STMT_116 \ "INSERT INTO wc_lock (wc_id, local_dir_relpath, locked_levels) " \ "VALUES (?1, ?2, ?3) " \ "" -#define STMT_SELECT_WC_LOCK 114 -#define STMT_114_INFO {"STMT_SELECT_WC_LOCK", NULL} -#define STMT_114 \ +#define STMT_SELECT_WC_LOCK 117 +#define STMT_117_INFO {"STMT_SELECT_WC_LOCK", NULL} +#define STMT_117 \ "SELECT locked_levels FROM wc_lock " \ "WHERE wc_id = ?1 AND local_dir_relpath = ?2 " \ "" -#define STMT_SELECT_ANCESTOR_WCLOCKS 115 -#define STMT_115_INFO {"STMT_SELECT_ANCESTOR_WCLOCKS", NULL} -#define STMT_115 \ +#define STMT_SELECT_ANCESTOR_WCLOCKS 118 +#define STMT_118_INFO {"STMT_SELECT_ANCESTOR_WCLOCKS", NULL} +#define STMT_118 \ "SELECT local_dir_relpath, locked_levels FROM wc_lock " \ "WHERE wc_id = ?1 " \ " AND ((local_dir_relpath >= ?3 AND local_dir_relpath <= ?2) " \ " OR local_dir_relpath = '') " \ "" -#define STMT_DELETE_WC_LOCK 116 -#define STMT_116_INFO {"STMT_DELETE_WC_LOCK", NULL} -#define STMT_116 \ +#define STMT_DELETE_WC_LOCK 119 +#define STMT_119_INFO {"STMT_DELETE_WC_LOCK", NULL} +#define STMT_119 \ "DELETE FROM wc_lock " \ "WHERE wc_id = ?1 AND local_dir_relpath = ?2 " \ "" -#define STMT_FIND_WC_LOCK 117 -#define STMT_117_INFO {"STMT_FIND_WC_LOCK", NULL} -#define STMT_117 \ +#define STMT_FIND_WC_LOCK 120 +#define STMT_120_INFO {"STMT_FIND_WC_LOCK", NULL} +#define STMT_120 \ "SELECT local_dir_relpath FROM wc_lock " \ "WHERE wc_id = ?1 " \ " AND (((local_dir_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_dir_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_DELETE_WC_LOCK_ORPHAN 118 -#define STMT_118_INFO {"STMT_DELETE_WC_LOCK_ORPHAN", NULL} -#define STMT_118 \ +#define STMT_FIND_CONFLICT_DESCENDANT 121 +#define STMT_121_INFO {"STMT_FIND_CONFLICT_DESCENDANT", NULL} +#define STMT_121 \ + "SELECT 1 FROM actual_node " \ + "WHERE wc_id = ?1 " \ + " AND local_relpath > (?2 || '/') " \ + " AND local_relpath < (?2 || '0') " \ + " AND conflict_data IS NOT NULL " \ + "LIMIT 1 " \ + "" + +#define STMT_DELETE_WC_LOCK_ORPHAN 122 +#define STMT_122_INFO {"STMT_DELETE_WC_LOCK_ORPHAN", NULL} +#define STMT_122 \ "DELETE FROM wc_lock " \ "WHERE wc_id = ?1 AND local_dir_relpath = ?2 " \ "AND NOT EXISTS (SELECT 1 FROM nodes " \ @@ -1181,9 +1259,9 @@ " AND nodes.local_relpath = wc_lock.local_dir_relpath) " \ "" -#define STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE 119 -#define STMT_119_INFO {"STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE", NULL} -#define STMT_119 \ +#define STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE 123 +#define STMT_123_INFO {"STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE", NULL} +#define STMT_123 \ "DELETE FROM wc_lock " \ "WHERE wc_id = ?1 " \ " AND (local_dir_relpath = ?2 " \ @@ -1193,9 +1271,9 @@ " AND nodes.local_relpath = wc_lock.local_dir_relpath) " \ "" -#define STMT_APPLY_CHANGES_TO_BASE_NODE 120 -#define STMT_120_INFO {"STMT_APPLY_CHANGES_TO_BASE_NODE", NULL} -#define STMT_120 \ +#define STMT_APPLY_CHANGES_TO_BASE_NODE 124 +#define STMT_124_INFO {"STMT_APPLY_CHANGES_TO_BASE_NODE", NULL} +#define STMT_124 \ "INSERT OR REPLACE INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \ " revision, presence, depth, kind, changed_revision, changed_date, " \ @@ -1209,77 +1287,70 @@ " AND op_depth = 0)) " \ "" -#define STMT_INSTALL_WORKING_NODE_FOR_DELETE 121 -#define STMT_121_INFO {"STMT_INSTALL_WORKING_NODE_FOR_DELETE", NULL} -#define STMT_121 \ - "INSERT OR REPLACE INTO nodes ( " \ +#define STMT_INSTALL_WORKING_NODE_FOR_DELETE 125 +#define STMT_125_INFO {"STMT_INSTALL_WORKING_NODE_FOR_DELETE", NULL} +#define STMT_125 \ + "INSERT INTO nodes ( " \ " wc_id, local_relpath, op_depth, " \ " parent_relpath, presence, kind) " \ "VALUES(?1, ?2, ?3, ?4, 'base-deleted', ?5) " \ "" -#define STMT_DELETE_NO_LOWER_LAYER 122 -#define STMT_122_INFO {"STMT_DELETE_NO_LOWER_LAYER", NULL} -#define STMT_122 \ - "DELETE FROM nodes " \ - " WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ - " AND op_depth = ?3 " \ - " AND NOT EXISTS (SELECT 1 FROM nodes n " \ - " WHERE n.wc_id = ?1 " \ - " AND n.local_relpath = nodes.local_relpath " \ - " AND n.op_depth = ?4 " \ - " AND n.presence IN ('normal', 'incomplete')) " \ - "" - -#define STMT_REPLACE_WITH_BASE_DELETED 123 -#define STMT_123_INFO {"STMT_REPLACE_WITH_BASE_DELETED", NULL} -#define STMT_123 \ +#define STMT_REPLACE_WITH_BASE_DELETED 126 +#define STMT_126_INFO {"STMT_REPLACE_WITH_BASE_DELETED", NULL} +#define STMT_126 \ "INSERT OR REPLACE INTO nodes (wc_id, local_relpath, op_depth, parent_relpath, " \ " kind, moved_to, presence) " \ "SELECT wc_id, local_relpath, op_depth, parent_relpath, " \ " kind, moved_to, 'base-deleted' " \ " FROM nodes " \ " WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ + " AND local_relpath = ?2 " \ " AND op_depth = ?3 " \ "" -#define STMT_INSERT_DELETE_FROM_NODE_RECURSIVE 124 -#define STMT_124_INFO {"STMT_INSERT_DELETE_FROM_NODE_RECURSIVE", NULL} -#define STMT_124 \ +#define STMT_INSERT_DELETE_FROM_NODE_RECURSIVE 127 +#define STMT_127_INFO {"STMT_INSERT_DELETE_FROM_NODE_RECURSIVE", NULL} +#define STMT_127 \ "INSERT INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, presence, kind) " \ "SELECT wc_id, local_relpath, ?4 , parent_relpath, 'base-deleted', " \ " kind " \ "FROM nodes " \ + "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ + "UNION ALL " \ + "SELECT wc_id, local_relpath, ?4 , parent_relpath, 'base-deleted', " \ + " kind " \ + "FROM nodes " \ "WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 " \ - " OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ + " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ " AND op_depth = ?3 " \ " AND presence NOT IN ('base-deleted', 'not-present', 'excluded', 'server-excluded') " \ " AND file_external IS NULL " \ + "ORDER BY local_relpath " \ "" -#define STMT_INSERT_WORKING_NODE_FROM_BASE_COPY 125 -#define STMT_125_INFO {"STMT_INSERT_WORKING_NODE_FROM_BASE_COPY", NULL} -#define STMT_125 \ - "INSERT INTO nodes ( " \ +#define STMT_INSERT_WORKING_NODE_FROM_BASE_COPY 128 +#define STMT_128_INFO {"STMT_INSERT_WORKING_NODE_FROM_BASE_COPY", NULL} +#define STMT_128 \ + "INSERT OR REPLACE INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \ " revision, presence, depth, kind, changed_revision, changed_date, " \ " changed_author, checksum, properties, translated_size, last_mod_time, " \ - " symlink_target ) " \ + " symlink_target, moved_to ) " \ "SELECT wc_id, local_relpath, ?3 , parent_relpath, repos_id, " \ " repos_path, revision, presence, depth, kind, changed_revision, " \ " changed_date, changed_author, checksum, properties, translated_size, " \ - " last_mod_time, symlink_target " \ + " last_mod_time, symlink_target, " \ + " (SELECT moved_to FROM nodes " \ + " WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3) moved_to " \ "FROM nodes " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_INSERT_DELETE_FROM_BASE 126 -#define STMT_126_INFO {"STMT_INSERT_DELETE_FROM_BASE", NULL} -#define STMT_126 \ +#define STMT_INSERT_DELETE_FROM_BASE 129 +#define STMT_129_INFO {"STMT_INSERT_DELETE_FROM_BASE", NULL} +#define STMT_129 \ "INSERT INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, presence, kind) " \ "SELECT wc_id, local_relpath, ?3 , parent_relpath, " \ @@ -1288,34 +1359,52 @@ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE 127 -#define STMT_127_INFO {"STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE", NULL} -#define STMT_127 \ +#define STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE 130 +#define STMT_130_INFO {"STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE", NULL} +#define STMT_130 \ "UPDATE nodes SET op_depth = ?3 + 1 " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ " AND op_depth = ?3 " \ "" -#define STMT_UPDATE_OP_DEPTH_RECURSIVE 128 -#define STMT_128_INFO {"STMT_UPDATE_OP_DEPTH_RECURSIVE", NULL} -#define STMT_128 \ - "UPDATE nodes SET op_depth = ?4, moved_here = NULL " \ - "WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ - " AND op_depth = ?3 " \ +#define STMT_COPY_OP_DEPTH_RECURSIVE 131 +#define STMT_131_INFO {"STMT_COPY_OP_DEPTH_RECURSIVE", NULL} +#define STMT_131 \ + "INSERT INTO nodes ( " \ + " wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, " \ + " revision, presence, depth, kind, changed_revision, changed_date, " \ + " changed_author, checksum, properties, translated_size, last_mod_time, " \ + " symlink_target, moved_here, moved_to ) " \ + "SELECT " \ + " wc_id, local_relpath, ?4, parent_relpath, repos_id, " \ + " repos_path, revision, presence, depth, kind, changed_revision, " \ + " changed_date, changed_author, checksum, properties, translated_size, " \ + " last_mod_time, symlink_target, NULL, NULL " \ + "FROM nodes " \ + "WHERE wc_id = ?1 AND op_depth = ?3 AND local_relpath = ?2 " \ + "UNION ALL " \ + "SELECT " \ + " wc_id, local_relpath, ?4, parent_relpath, repos_id, " \ + " repos_path, revision, presence, depth, kind, changed_revision, " \ + " changed_date, changed_author, checksum, properties, translated_size, " \ + " last_mod_time, symlink_target, NULL, NULL " \ + "FROM nodes " \ + "WHERE wc_id = ?1 AND op_depth = ?3 " \ + " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + "ORDER BY local_relpath " \ "" -#define STMT_DOES_NODE_EXIST 129 -#define STMT_129_INFO {"STMT_DOES_NODE_EXIST", NULL} -#define STMT_129 \ +#define STMT_DOES_NODE_EXIST 132 +#define STMT_132_INFO {"STMT_DOES_NODE_EXIST", NULL} +#define STMT_132 \ "SELECT 1 FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 " \ "LIMIT 1 " \ "" -#define STMT_HAS_SERVER_EXCLUDED_DESCENDANTS 130 -#define STMT_130_INFO {"STMT_HAS_SERVER_EXCLUDED_DESCENDANTS", NULL} -#define STMT_130 \ +#define STMT_HAS_SERVER_EXCLUDED_DESCENDANTS 133 +#define STMT_133_INFO {"STMT_HAS_SERVER_EXCLUDED_DESCENDANTS", NULL} +#define STMT_133 \ "SELECT local_relpath FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ @@ -1323,9 +1412,9 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_ALL_EXCLUDED_DESCENDANTS 131 -#define STMT_131_INFO {"STMT_SELECT_ALL_EXCLUDED_DESCENDANTS", NULL} -#define STMT_131 \ +#define STMT_SELECT_ALL_EXCLUDED_DESCENDANTS 134 +#define STMT_134_INFO {"STMT_SELECT_ALL_EXCLUDED_DESCENDANTS", NULL} +#define STMT_134 \ "SELECT local_relpath FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ @@ -1333,9 +1422,9 @@ " AND (presence = 'server-excluded' OR presence = 'excluded') " \ "" -#define STMT_INSERT_WORKING_NODE_COPY_FROM 132 -#define STMT_132_INFO {"STMT_INSERT_WORKING_NODE_COPY_FROM", NULL} -#define STMT_132 \ +#define STMT_INSERT_WORKING_NODE_COPY_FROM 135 +#define STMT_135_INFO {"STMT_INSERT_WORKING_NODE_COPY_FROM", NULL} +#define STMT_135 \ "INSERT OR REPLACE INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, repos_id, " \ " repos_path, revision, presence, depth, moved_here, kind, changed_revision, " \ @@ -1354,9 +1443,9 @@ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH 133 -#define STMT_133_INFO {"STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH", NULL} -#define STMT_133 \ +#define STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH 136 +#define STMT_136_INFO {"STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH", NULL} +#define STMT_136 \ "INSERT OR REPLACE INTO nodes ( " \ " wc_id, local_relpath, op_depth, parent_relpath, repos_id, " \ " repos_path, revision, presence, depth, moved_here, kind, changed_revision, " \ @@ -1375,49 +1464,49 @@ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?7 " \ "" -#define STMT_UPDATE_BASE_REVISION 134 -#define STMT_134_INFO {"STMT_UPDATE_BASE_REVISION", NULL} -#define STMT_134 \ +#define STMT_UPDATE_BASE_REVISION 137 +#define STMT_137_INFO {"STMT_UPDATE_BASE_REVISION", NULL} +#define STMT_137 \ "UPDATE nodes SET revision = ?3 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_UPDATE_BASE_REPOS 135 -#define STMT_135_INFO {"STMT_UPDATE_BASE_REPOS", NULL} -#define STMT_135 \ +#define STMT_UPDATE_BASE_REPOS 138 +#define STMT_138_INFO {"STMT_UPDATE_BASE_REPOS", NULL} +#define STMT_138 \ "UPDATE nodes SET repos_id = ?3, repos_path = ?4 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 " \ "" -#define STMT_ACTUAL_HAS_CHILDREN 136 -#define STMT_136_INFO {"STMT_ACTUAL_HAS_CHILDREN", NULL} -#define STMT_136 \ +#define STMT_ACTUAL_HAS_CHILDREN 139 +#define STMT_139_INFO {"STMT_ACTUAL_HAS_CHILDREN", NULL} +#define STMT_139 \ "SELECT 1 FROM actual_node " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 " \ "LIMIT 1 " \ "" -#define STMT_INSERT_EXTERNAL 137 -#define STMT_137_INFO {"STMT_INSERT_EXTERNAL", NULL} -#define STMT_137 \ +#define STMT_INSERT_EXTERNAL 140 +#define STMT_140_INFO {"STMT_INSERT_EXTERNAL", NULL} +#define STMT_140 \ "INSERT OR REPLACE INTO externals ( " \ " wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath, " \ " repos_id, def_repos_relpath, def_operational_revision, def_revision) " \ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10) " \ "" -#define STMT_SELECT_EXTERNAL_INFO 138 -#define STMT_138_INFO {"STMT_SELECT_EXTERNAL_INFO", NULL} -#define STMT_138 \ +#define STMT_SELECT_EXTERNAL_INFO 141 +#define STMT_141_INFO {"STMT_SELECT_EXTERNAL_INFO", NULL} +#define STMT_141 \ "SELECT presence, kind, def_local_relpath, repos_id, " \ " def_repos_relpath, def_operational_revision, def_revision " \ "FROM externals WHERE wc_id = ?1 AND local_relpath = ?2 " \ "LIMIT 1 " \ "" -#define STMT_DELETE_FILE_EXTERNALS 139 -#define STMT_139_INFO {"STMT_DELETE_FILE_EXTERNALS", NULL} -#define STMT_139 \ +#define STMT_DELETE_FILE_EXTERNALS 142 +#define STMT_142_INFO {"STMT_DELETE_FILE_EXTERNALS", NULL} +#define STMT_142 \ "DELETE FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ @@ -1425,26 +1514,26 @@ " AND file_external IS NOT NULL " \ "" -#define STMT_DELETE_FILE_EXTERNAL_REGISTATIONS 140 -#define STMT_140_INFO {"STMT_DELETE_FILE_EXTERNAL_REGISTATIONS", NULL} -#define STMT_140 \ +#define STMT_DELETE_FILE_EXTERNAL_REGISTATIONS 143 +#define STMT_143_INFO {"STMT_DELETE_FILE_EXTERNAL_REGISTATIONS", NULL} +#define STMT_143 \ "DELETE FROM externals " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ " AND kind != 'dir' " \ "" -#define STMT_DELETE_EXTERNAL_REGISTATIONS 141 -#define STMT_141_INFO {"STMT_DELETE_EXTERNAL_REGISTATIONS", NULL} -#define STMT_141 \ +#define STMT_DELETE_EXTERNAL_REGISTATIONS 144 +#define STMT_144_INFO {"STMT_DELETE_EXTERNAL_REGISTATIONS", NULL} +#define STMT_144 \ "DELETE FROM externals " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW 142 -#define STMT_142_INFO {"STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW", NULL} -#define STMT_142 \ +#define STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW 145 +#define STMT_145_INFO {"STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW", NULL} +#define STMT_145 \ "SELECT local_relpath, kind, def_repos_relpath, " \ " (SELECT root FROM repository AS r WHERE r.id = e.repos_id) " \ "FROM externals e " \ @@ -1462,9 +1551,9 @@ " AND nodes.local_relpath = e.parent_relpath)) " \ "" -#define STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW 143 -#define STMT_143_INFO {"STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW", NULL} -#define STMT_143 \ +#define STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW 146 +#define STMT_146_INFO {"STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW", NULL} +#define STMT_146 \ "SELECT local_relpath, kind, def_repos_relpath, " \ " (SELECT root FROM repository AS r WHERE r.id = e.repos_id) " \ "FROM externals e " \ @@ -1483,25 +1572,25 @@ " AND nodes.local_relpath = e.parent_relpath)) " \ "" -#define STMT_SELECT_EXTERNALS_DEFINED 144 -#define STMT_144_INFO {"STMT_SELECT_EXTERNALS_DEFINED", NULL} -#define STMT_144 \ +#define STMT_SELECT_EXTERNALS_DEFINED 147 +#define STMT_147_INFO {"STMT_SELECT_EXTERNALS_DEFINED", NULL} +#define STMT_147 \ "SELECT local_relpath, def_local_relpath " \ "FROM externals " \ "WHERE (wc_id = ?1 AND def_local_relpath = ?2) " \ " OR (wc_id = ?1 AND (((def_local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((def_local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ "" -#define STMT_DELETE_EXTERNAL 145 -#define STMT_145_INFO {"STMT_DELETE_EXTERNAL", NULL} -#define STMT_145 \ +#define STMT_DELETE_EXTERNAL 148 +#define STMT_148_INFO {"STMT_DELETE_EXTERNAL", NULL} +#define STMT_148 \ "DELETE FROM externals " \ "WHERE wc_id = ?1 AND local_relpath = ?2 " \ "" -#define STMT_SELECT_EXTERNAL_PROPERTIES 146 -#define STMT_146_INFO {"STMT_SELECT_EXTERNAL_PROPERTIES", NULL} -#define STMT_146 \ +#define STMT_SELECT_EXTERNAL_PROPERTIES 149 +#define STMT_149_INFO {"STMT_SELECT_EXTERNAL_PROPERTIES", NULL} +#define STMT_149 \ "SELECT IFNULL((SELECT properties FROM actual_node a " \ " WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath), " \ " properties), " \ @@ -1519,9 +1608,9 @@ " AND kind = 'dir' AND presence IN ('normal', 'incomplete') " \ "" -#define STMT_SELECT_CURRENT_PROPS_RECURSIVE 147 -#define STMT_147_INFO {"STMT_SELECT_CURRENT_PROPS_RECURSIVE", NULL} -#define STMT_147 \ +#define STMT_SELECT_CURRENT_PROPS_RECURSIVE 150 +#define STMT_150_INFO {"STMT_SELECT_CURRENT_PROPS_RECURSIVE", NULL} +#define STMT_150 \ "SELECT IFNULL((SELECT properties FROM actual_node a " \ " WHERE a.wc_id = ?1 AND A.local_relpath = n.local_relpath), " \ " properties), " \ @@ -1531,57 +1620,44 @@ " OR (wc_id = ?1 AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ "" -#define STMT_PRAGMA_LOCKING_MODE 148 -#define STMT_148_INFO {"STMT_PRAGMA_LOCKING_MODE", NULL} -#define STMT_148 \ - "PRAGMA locking_mode = exclusive " \ +#define STMT_PRAGMA_LOCKING_MODE 151 +#define STMT_151_INFO {"STMT_PRAGMA_LOCKING_MODE", NULL} +#define STMT_151 \ + "PRAGMA locking_mode = exclusive; " \ + "PRAGMA journal_mode = DELETE " \ "" -#define STMT_INSERT_ACTUAL_NODE 149 -#define STMT_149_INFO {"STMT_INSERT_ACTUAL_NODE", NULL} -#define STMT_149 \ +#define STMT_INSERT_ACTUAL_NODE 152 +#define STMT_152_INFO {"STMT_INSERT_ACTUAL_NODE", NULL} +#define STMT_152 \ "INSERT OR REPLACE INTO actual_node ( " \ " wc_id, local_relpath, parent_relpath, properties, changelist, conflict_data) " \ "VALUES (?1, ?2, ?3, ?4, ?5, ?6) " \ "" -#define STMT_UPDATE_ACTUAL_CONFLICT_DATA 150 -#define STMT_150_INFO {"STMT_UPDATE_ACTUAL_CONFLICT_DATA", NULL} -#define STMT_150 \ - "UPDATE actual_node SET conflict_data = ?3 " \ - "WHERE wc_id = ?1 AND local_relpath = ?2 " \ - "" - -#define STMT_INSERT_ACTUAL_CONFLICT_DATA 151 -#define STMT_151_INFO {"STMT_INSERT_ACTUAL_CONFLICT_DATA", NULL} -#define STMT_151 \ - "INSERT INTO actual_node (wc_id, local_relpath, conflict_data, parent_relpath) " \ - "VALUES (?1, ?2, ?3, ?4) " \ - "" - -#define STMT_SELECT_ALL_FILES 152 -#define STMT_152_INFO {"STMT_SELECT_ALL_FILES", NULL} -#define STMT_152 \ +#define STMT_SELECT_ALL_FILES 153 +#define STMT_153_INFO {"STMT_SELECT_ALL_FILES", NULL} +#define STMT_153 \ "SELECT local_relpath FROM nodes_current " \ "WHERE wc_id = ?1 AND parent_relpath = ?2 AND kind = 'file' " \ "" -#define STMT_UPDATE_NODE_PROPS 153 -#define STMT_153_INFO {"STMT_UPDATE_NODE_PROPS", NULL} -#define STMT_153 \ +#define STMT_UPDATE_NODE_PROPS 154 +#define STMT_154_INFO {"STMT_UPDATE_NODE_PROPS", NULL} +#define STMT_154 \ "UPDATE nodes SET properties = ?4 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ "" -#define STMT_PRAGMA_TABLE_INFO_NODES 154 -#define STMT_154_INFO {"STMT_PRAGMA_TABLE_INFO_NODES", NULL} -#define STMT_154 \ +#define STMT_PRAGMA_TABLE_INFO_NODES 155 +#define STMT_155_INFO {"STMT_PRAGMA_TABLE_INFO_NODES", NULL} +#define STMT_155 \ "PRAGMA table_info(\"NODES\") " \ "" -#define STMT_CREATE_TARGET_PROP_CACHE 155 -#define STMT_155_INFO {"STMT_CREATE_TARGET_PROP_CACHE", NULL} -#define STMT_155 \ +#define STMT_CREATE_TARGET_PROP_CACHE 156 +#define STMT_156_INFO {"STMT_CREATE_TARGET_PROP_CACHE", NULL} +#define STMT_156 \ "DROP TABLE IF EXISTS target_prop_cache; " \ "CREATE TEMPORARY TABLE target_prop_cache ( " \ " local_relpath TEXT NOT NULL PRIMARY KEY, " \ @@ -1590,9 +1666,9 @@ "); " \ "" -#define STMT_CACHE_TARGET_PROPS 156 -#define STMT_156_INFO {"STMT_CACHE_TARGET_PROPS", NULL} -#define STMT_156 \ +#define STMT_CACHE_TARGET_PROPS 157 +#define STMT_157_INFO {"STMT_CACHE_TARGET_PROPS", NULL} +#define STMT_157 \ "INSERT INTO target_prop_cache(local_relpath, kind, properties) " \ " SELECT n.local_relpath, n.kind, " \ " IFNULL((SELECT properties FROM actual_node AS a " \ @@ -1611,9 +1687,9 @@ " ORDER BY t.local_relpath " \ "" -#define STMT_CACHE_TARGET_PRISTINE_PROPS 157 -#define STMT_157_INFO {"STMT_CACHE_TARGET_PRISTINE_PROPS", NULL} -#define STMT_157 \ +#define STMT_CACHE_TARGET_PRISTINE_PROPS 158 +#define STMT_158_INFO {"STMT_CACHE_TARGET_PRISTINE_PROPS", NULL} +#define STMT_158 \ "INSERT INTO target_prop_cache(local_relpath, kind, properties) " \ " SELECT n.local_relpath, n.kind, " \ " CASE n.presence " \ @@ -1638,22 +1714,22 @@ " ORDER BY t.local_relpath " \ "" -#define STMT_SELECT_ALL_TARGET_PROP_CACHE 158 -#define STMT_158_INFO {"STMT_SELECT_ALL_TARGET_PROP_CACHE", NULL} -#define STMT_158 \ +#define STMT_SELECT_ALL_TARGET_PROP_CACHE 159 +#define STMT_159_INFO {"STMT_SELECT_ALL_TARGET_PROP_CACHE", NULL} +#define STMT_159 \ "SELECT local_relpath, properties FROM target_prop_cache " \ "ORDER BY local_relpath " \ "" -#define STMT_DROP_TARGET_PROP_CACHE 159 -#define STMT_159_INFO {"STMT_DROP_TARGET_PROP_CACHE", NULL} -#define STMT_159 \ +#define STMT_DROP_TARGET_PROP_CACHE 160 +#define STMT_160_INFO {"STMT_DROP_TARGET_PROP_CACHE", NULL} +#define STMT_160 \ "DROP TABLE target_prop_cache; " \ "" -#define STMT_CREATE_REVERT_LIST 160 -#define STMT_160_INFO {"STMT_CREATE_REVERT_LIST", NULL} -#define STMT_160 \ +#define STMT_CREATE_REVERT_LIST 161 +#define STMT_161_INFO {"STMT_CREATE_REVERT_LIST", NULL} +#define STMT_161 \ "DROP TABLE IF EXISTS revert_list; " \ "CREATE TEMPORARY TABLE revert_list ( " \ " local_relpath TEXT NOT NULL, " \ @@ -1687,8 +1763,9 @@ " WHERE n.wc_id = OLD.wc_id " \ " AND n.local_relpath = OLD.local_relpath) " \ " THEN 1 " \ - " ELSE NULL " \ - " END; " \ + " END notify " \ + " WHERE OLD.conflict_data IS NOT NULL " \ + " OR notify IS NOT NULL; " \ "END; " \ "DROP TRIGGER IF EXISTS trigger_revert_list_actual_update; " \ "CREATE TEMPORARY TRIGGER trigger_revert_list_actual_update " \ @@ -1704,31 +1781,32 @@ " WHERE n.wc_id = OLD.wc_id " \ " AND n.local_relpath = OLD.local_relpath) " \ " THEN 1 " \ - " ELSE NULL " \ - " END; " \ + " END notify " \ + " WHERE OLD.conflict_data IS NOT NULL " \ + " OR notify IS NOT NULL; " \ "END " \ "" -#define STMT_DROP_REVERT_LIST_TRIGGERS 161 -#define STMT_161_INFO {"STMT_DROP_REVERT_LIST_TRIGGERS", NULL} -#define STMT_161 \ +#define STMT_DROP_REVERT_LIST_TRIGGERS 162 +#define STMT_162_INFO {"STMT_DROP_REVERT_LIST_TRIGGERS", NULL} +#define STMT_162 \ "DROP TRIGGER trigger_revert_list_nodes; " \ "DROP TRIGGER trigger_revert_list_actual_delete; " \ "DROP TRIGGER trigger_revert_list_actual_update " \ "" -#define STMT_SELECT_REVERT_LIST 162 -#define STMT_162_INFO {"STMT_SELECT_REVERT_LIST", NULL} -#define STMT_162 \ +#define STMT_SELECT_REVERT_LIST 163 +#define STMT_163_INFO {"STMT_SELECT_REVERT_LIST", NULL} +#define STMT_163 \ "SELECT actual, notify, kind, op_depth, repos_id, conflict_data " \ "FROM revert_list " \ "WHERE local_relpath = ?1 " \ "ORDER BY actual DESC " \ "" -#define STMT_SELECT_REVERT_LIST_COPIED_CHILDREN 163 -#define STMT_163_INFO {"STMT_SELECT_REVERT_LIST_COPIED_CHILDREN", NULL} -#define STMT_163 \ +#define STMT_SELECT_REVERT_LIST_COPIED_CHILDREN 164 +#define STMT_164_INFO {"STMT_SELECT_REVERT_LIST_COPIED_CHILDREN", NULL} +#define STMT_164 \ "SELECT local_relpath, kind " \ "FROM revert_list " \ "WHERE (((local_relpath) > (CASE (?1) WHEN '' THEN '' ELSE (?1) || '/' END)) AND ((local_relpath) < CASE (?1) WHEN '' THEN X'FFFF' ELSE (?1) || '0' END)) " \ @@ -1737,113 +1815,125 @@ "ORDER BY local_relpath " \ "" -#define STMT_DELETE_REVERT_LIST 164 -#define STMT_164_INFO {"STMT_DELETE_REVERT_LIST", NULL} -#define STMT_164 \ - "DELETE FROM revert_list WHERE local_relpath = ?1 " \ - "" - -#define STMT_SELECT_REVERT_LIST_RECURSIVE 165 -#define STMT_165_INFO {"STMT_SELECT_REVERT_LIST_RECURSIVE", NULL} +#define STMT_DELETE_REVERT_LIST 165 +#define STMT_165_INFO {"STMT_DELETE_REVERT_LIST", NULL} #define STMT_165 \ - "SELECT DISTINCT local_relpath " \ - "FROM revert_list " \ - "WHERE (local_relpath = ?1 " \ - " OR (((local_relpath) > (CASE (?1) WHEN '' THEN '' ELSE (?1) || '/' END)) AND ((local_relpath) < CASE (?1) WHEN '' THEN X'FFFF' ELSE (?1) || '0' END))) " \ - " AND (notify OR actual = 0) " \ - "ORDER BY local_relpath " \ + "DELETE FROM revert_list WHERE local_relpath = ?1 " \ "" -#define STMT_DELETE_REVERT_LIST_RECURSIVE 166 -#define STMT_166_INFO {"STMT_DELETE_REVERT_LIST_RECURSIVE", NULL} +#define STMT_SELECT_REVERT_LIST_RECURSIVE 166 +#define STMT_166_INFO {"STMT_SELECT_REVERT_LIST_RECURSIVE", NULL} #define STMT_166 \ + "SELECT p.local_relpath, n.kind, a.notify, a.kind " \ + "FROM (SELECT DISTINCT local_relpath " \ + " FROM revert_list " \ + " WHERE (local_relpath = ?1 " \ + " OR (((local_relpath) > (CASE (?1) WHEN '' THEN '' ELSE (?1) || '/' END)) AND ((local_relpath) < CASE (?1) WHEN '' THEN X'FFFF' ELSE (?1) || '0' END)))) p " \ + "LEFT JOIN revert_list n ON n.local_relpath=p.local_relpath AND n.actual=0 " \ + "LEFT JOIN revert_list a ON a.local_relpath=p.local_relpath AND a.actual=1 " \ + "ORDER BY p.local_relpath " \ + "" + +#define STMT_DELETE_REVERT_LIST_RECURSIVE 167 +#define STMT_167_INFO {"STMT_DELETE_REVERT_LIST_RECURSIVE", NULL} +#define STMT_167 \ "DELETE FROM revert_list " \ "WHERE (local_relpath = ?1 " \ " OR (((local_relpath) > (CASE (?1) WHEN '' THEN '' ELSE (?1) || '/' END)) AND ((local_relpath) < CASE (?1) WHEN '' THEN X'FFFF' ELSE (?1) || '0' END))) " \ "" -#define STMT_DROP_REVERT_LIST 167 -#define STMT_167_INFO {"STMT_DROP_REVERT_LIST", NULL} -#define STMT_167 \ +#define STMT_DROP_REVERT_LIST 168 +#define STMT_168_INFO {"STMT_DROP_REVERT_LIST", NULL} +#define STMT_168 \ "DROP TABLE IF EXISTS revert_list " \ "" -#define STMT_CREATE_DELETE_LIST 168 -#define STMT_168_INFO {"STMT_CREATE_DELETE_LIST", NULL} -#define STMT_168 \ +#define STMT_CREATE_DELETE_LIST 169 +#define STMT_169_INFO {"STMT_CREATE_DELETE_LIST", NULL} +#define STMT_169 \ "DROP TABLE IF EXISTS delete_list; " \ "CREATE TEMPORARY TABLE delete_list ( " \ " local_relpath TEXT PRIMARY KEY NOT NULL UNIQUE " \ " ) " \ "" -#define STMT_INSERT_DELETE_LIST 169 -#define STMT_169_INFO {"STMT_INSERT_DELETE_LIST", NULL} -#define STMT_169 \ +#define STMT_INSERT_DELETE_LIST 170 +#define STMT_170_INFO {"STMT_INSERT_DELETE_LIST", NULL} +#define STMT_170 \ "INSERT INTO delete_list(local_relpath) " \ + "SELECT ?2 " \ + "UNION ALL " \ "SELECT local_relpath FROM nodes AS n " \ "WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 " \ - " OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ + " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ " AND op_depth >= ?3 " \ " AND op_depth = (SELECT MAX(s.op_depth) FROM nodes AS s " \ " WHERE s.wc_id = ?1 " \ " AND s.local_relpath = n.local_relpath) " \ " AND presence NOT IN ('base-deleted', 'not-present', 'excluded', 'server-excluded') " \ " AND file_external IS NULL " \ + "ORDER by local_relpath " \ "" -#define STMT_SELECT_DELETE_LIST 170 -#define STMT_170_INFO {"STMT_SELECT_DELETE_LIST", NULL} -#define STMT_170 \ +#define STMT_SELECT_DELETE_LIST 171 +#define STMT_171_INFO {"STMT_SELECT_DELETE_LIST", NULL} +#define STMT_171 \ "SELECT local_relpath FROM delete_list " \ "ORDER BY local_relpath " \ "" -#define STMT_FINALIZE_DELETE 171 -#define STMT_171_INFO {"STMT_FINALIZE_DELETE", NULL} -#define STMT_171 \ +#define STMT_FINALIZE_DELETE 172 +#define STMT_172_INFO {"STMT_FINALIZE_DELETE", NULL} +#define STMT_172 \ "DROP TABLE IF EXISTS delete_list " \ "" -#define STMT_CREATE_UPDATE_MOVE_LIST 172 -#define STMT_172_INFO {"STMT_CREATE_UPDATE_MOVE_LIST", NULL} -#define STMT_172 \ +#define STMT_CREATE_UPDATE_MOVE_LIST 173 +#define STMT_173_INFO {"STMT_CREATE_UPDATE_MOVE_LIST", NULL} +#define STMT_173 \ "DROP TABLE IF EXISTS update_move_list; " \ "CREATE TEMPORARY TABLE update_move_list ( " \ " local_relpath TEXT PRIMARY KEY NOT NULL UNIQUE, " \ " action INTEGER NOT NULL, " \ - " kind INTEGER NOT NULL, " \ + " kind TEXT NOT NULL, " \ " content_state INTEGER NOT NULL, " \ " prop_state INTEGER NOT NULL " \ " ) " \ "" -#define STMT_INSERT_UPDATE_MOVE_LIST 173 -#define STMT_173_INFO {"STMT_INSERT_UPDATE_MOVE_LIST", NULL} -#define STMT_173 \ +#define STMT_INSERT_UPDATE_MOVE_LIST 174 +#define STMT_174_INFO {"STMT_INSERT_UPDATE_MOVE_LIST", NULL} +#define STMT_174 \ "INSERT INTO update_move_list(local_relpath, action, kind, content_state, " \ " prop_state) " \ "VALUES (?1, ?2, ?3, ?4, ?5) " \ "" -#define STMT_SELECT_UPDATE_MOVE_LIST 174 -#define STMT_174_INFO {"STMT_SELECT_UPDATE_MOVE_LIST", NULL} -#define STMT_174 \ +#define STMT_SELECT_UPDATE_MOVE_LIST 175 +#define STMT_175_INFO {"STMT_SELECT_UPDATE_MOVE_LIST", NULL} +#define STMT_175 \ "SELECT local_relpath, action, kind, content_state, prop_state " \ "FROM update_move_list " \ "ORDER BY local_relpath " \ "" -#define STMT_FINALIZE_UPDATE_MOVE 175 -#define STMT_175_INFO {"STMT_FINALIZE_UPDATE_MOVE", NULL} -#define STMT_175 \ +#define STMT_FINALIZE_UPDATE_MOVE 176 +#define STMT_176_INFO {"STMT_FINALIZE_UPDATE_MOVE", NULL} +#define STMT_176 \ "DROP TABLE IF EXISTS update_move_list " \ "" -#define STMT_SELECT_MIN_MAX_REVISIONS 176 -#define STMT_176_INFO {"STMT_SELECT_MIN_MAX_REVISIONS", NULL} -#define STMT_176 \ +#define STMT_MOVE_NOTIFY_TO_REVERT 177 +#define STMT_177_INFO {"STMT_MOVE_NOTIFY_TO_REVERT", NULL} +#define STMT_177 \ + "INSERT INTO revert_list (local_relpath, notify, kind, actual) " \ + " SELECT local_relpath, 2, kind, 1 FROM update_move_list; " \ + "DROP TABLE update_move_list " \ + "" + +#define STMT_SELECT_MIN_MAX_REVISIONS 178 +#define STMT_178_INFO {"STMT_SELECT_MIN_MAX_REVISIONS", NULL} +#define STMT_178 \ "SELECT MIN(revision), MAX(revision), " \ " MIN(changed_revision), MAX(changed_revision) FROM nodes " \ " WHERE wc_id = ?1 " \ @@ -1854,9 +1944,9 @@ " AND op_depth = 0 " \ "" -#define STMT_HAS_SPARSE_NODES 177 -#define STMT_177_INFO {"STMT_HAS_SPARSE_NODES", NULL} -#define STMT_177 \ +#define STMT_HAS_SPARSE_NODES 179 +#define STMT_179_INFO {"STMT_HAS_SPARSE_NODES", NULL} +#define STMT_179 \ "SELECT 1 FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -1868,9 +1958,9 @@ "LIMIT 1 " \ "" -#define STMT_SUBTREE_HAS_TREE_MODIFICATIONS 178 -#define STMT_178_INFO {"STMT_SUBTREE_HAS_TREE_MODIFICATIONS", NULL} -#define STMT_178 \ +#define STMT_SUBTREE_HAS_TREE_MODIFICATIONS 180 +#define STMT_180_INFO {"STMT_SUBTREE_HAS_TREE_MODIFICATIONS", NULL} +#define STMT_180 \ "SELECT 1 FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -1879,9 +1969,9 @@ "LIMIT 1 " \ "" -#define STMT_SUBTREE_HAS_PROP_MODIFICATIONS 179 -#define STMT_179_INFO {"STMT_SUBTREE_HAS_PROP_MODIFICATIONS", NULL} -#define STMT_179 \ +#define STMT_SUBTREE_HAS_PROP_MODIFICATIONS 181 +#define STMT_181_INFO {"STMT_SUBTREE_HAS_PROP_MODIFICATIONS", NULL} +#define STMT_181 \ "SELECT 1 FROM actual_node " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -1890,9 +1980,9 @@ "LIMIT 1 " \ "" -#define STMT_HAS_SWITCHED 180 -#define STMT_180_INFO {"STMT_HAS_SWITCHED", NULL} -#define STMT_180 \ +#define STMT_HAS_SWITCHED 182 +#define STMT_182_INFO {"STMT_HAS_SWITCHED", NULL} +#define STMT_182 \ "SELECT 1 " \ "FROM nodes " \ "WHERE wc_id = ?1 " \ @@ -1904,60 +1994,47 @@ "LIMIT 1 " \ "" -#define STMT_SELECT_BASE_FILES_RECURSIVE 181 -#define STMT_181_INFO {"STMT_SELECT_BASE_FILES_RECURSIVE", NULL} -#define STMT_181 \ - "SELECT local_relpath, translated_size, last_mod_time FROM nodes AS n " \ - "WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 " \ - " OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ - " AND op_depth = 0 " \ - " AND kind='file' " \ - " AND presence='normal' " \ - " AND file_external IS NULL " \ - "" - -#define STMT_SELECT_MOVED_FROM_RELPATH 182 -#define STMT_182_INFO {"STMT_SELECT_MOVED_FROM_RELPATH", NULL} -#define STMT_182 \ +#define STMT_SELECT_MOVED_FROM_RELPATH 183 +#define STMT_183_INFO {"STMT_SELECT_MOVED_FROM_RELPATH", NULL} +#define STMT_183 \ "SELECT local_relpath, op_depth FROM nodes " \ "WHERE wc_id = ?1 AND moved_to = ?2 AND op_depth > 0 " \ "" -#define STMT_UPDATE_MOVED_TO_RELPATH 183 -#define STMT_183_INFO {"STMT_UPDATE_MOVED_TO_RELPATH", NULL} -#define STMT_183 \ +#define STMT_UPDATE_MOVED_TO_RELPATH 184 +#define STMT_184_INFO {"STMT_UPDATE_MOVED_TO_RELPATH", NULL} +#define STMT_184 \ "UPDATE nodes SET moved_to = ?4 " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ "" -#define STMT_CLEAR_MOVED_TO_RELPATH 184 -#define STMT_184_INFO {"STMT_CLEAR_MOVED_TO_RELPATH", NULL} -#define STMT_184 \ +#define STMT_CLEAR_MOVED_TO_RELPATH 185 +#define STMT_185_INFO {"STMT_CLEAR_MOVED_TO_RELPATH", NULL} +#define STMT_185 \ "UPDATE nodes SET moved_to = NULL " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 " \ "" -#define STMT_CLEAR_MOVED_HERE_RECURSIVE 185 -#define STMT_185_INFO {"STMT_CLEAR_MOVED_HERE_RECURSIVE", NULL} -#define STMT_185 \ +#define STMT_CLEAR_MOVED_HERE_RECURSIVE 186 +#define STMT_186_INFO {"STMT_CLEAR_MOVED_HERE_RECURSIVE", NULL} +#define STMT_186 \ "UPDATE nodes SET moved_here = NULL " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ " AND op_depth = ?3 " \ "" -#define STMT_SELECT_MOVED_HERE_CHILDREN 186 -#define STMT_186_INFO {"STMT_SELECT_MOVED_HERE_CHILDREN", NULL} -#define STMT_186 \ +#define STMT_SELECT_MOVED_HERE_CHILDREN 187 +#define STMT_187_INFO {"STMT_SELECT_MOVED_HERE_CHILDREN", NULL} +#define STMT_187 \ "SELECT moved_to, local_relpath FROM nodes " \ "WHERE wc_id = ?1 AND op_depth > 0 " \ " AND (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_SELECT_MOVED_FOR_DELETE 187 -#define STMT_187_INFO {"STMT_SELECT_MOVED_FOR_DELETE", NULL} -#define STMT_187 \ +#define STMT_SELECT_MOVED_FOR_DELETE 188 +#define STMT_188_INFO {"STMT_SELECT_MOVED_FOR_DELETE", NULL} +#define STMT_188 \ "SELECT local_relpath, moved_to, op_depth, " \ " (SELECT CASE WHEN r.moved_here THEN r.op_depth END FROM nodes r " \ " WHERE r.wc_id = ?1 " \ @@ -1971,9 +2048,9 @@ " AND op_depth >= ?3 " \ "" -#define STMT_SELECT_MOVED_FROM_FOR_DELETE 188 -#define STMT_188_INFO {"STMT_SELECT_MOVED_FROM_FOR_DELETE", NULL} -#define STMT_188 \ +#define STMT_SELECT_MOVED_FROM_FOR_DELETE 189 +#define STMT_189_INFO {"STMT_SELECT_MOVED_FROM_FOR_DELETE", NULL} +#define STMT_189 \ "SELECT local_relpath, op_depth, " \ " (SELECT CASE WHEN r.moved_here THEN r.op_depth END FROM nodes r " \ " WHERE r.wc_id = ?1 " \ @@ -1984,48 +2061,48 @@ "WHERE wc_id = ?1 AND moved_to = ?2 AND op_depth > 0 " \ "" -#define STMT_UPDATE_MOVED_TO_DESCENDANTS 189 -#define STMT_189_INFO {"STMT_UPDATE_MOVED_TO_DESCENDANTS", NULL} -#define STMT_189 \ +#define STMT_UPDATE_MOVED_TO_DESCENDANTS 190 +#define STMT_190_INFO {"STMT_UPDATE_MOVED_TO_DESCENDANTS", NULL} +#define STMT_190 \ "UPDATE nodes SET moved_to = (CASE WHEN (?2) = '' THEN (CASE WHEN (?3) = '' THEN (moved_to) WHEN (moved_to) = '' THEN (?3) ELSE (?3) || '/' || (moved_to) END) WHEN (?3) = '' THEN (CASE WHEN (?2) = '' THEN (moved_to) WHEN SUBSTR((moved_to), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(moved_to) THEN '' WHEN SUBSTR((moved_to), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((moved_to), LENGTH(?2)+2) END END) WHEN SUBSTR((moved_to), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(moved_to) THEN (?3) WHEN SUBSTR((moved_to), LENGTH(?2)+1, 1) = '/' THEN (?3) || SUBSTR((moved_to), LENGTH(?2)+1) END END) " \ " WHERE wc_id = ?1 " \ " AND (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_CLEAR_MOVED_TO_DESCENDANTS 190 -#define STMT_190_INFO {"STMT_CLEAR_MOVED_TO_DESCENDANTS", NULL} -#define STMT_190 \ +#define STMT_CLEAR_MOVED_TO_DESCENDANTS 191 +#define STMT_191_INFO {"STMT_CLEAR_MOVED_TO_DESCENDANTS", NULL} +#define STMT_191 \ "UPDATE nodes SET moved_to = NULL " \ " WHERE wc_id = ?1 " \ " AND (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_SELECT_MOVED_PAIR2 191 -#define STMT_191_INFO {"STMT_SELECT_MOVED_PAIR2", NULL} -#define STMT_191 \ - "SELECT local_relpath, moved_to, op_depth FROM nodes " \ - "WHERE wc_id = ?1 " \ - " AND (local_relpath = ?2 OR (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ - " AND moved_to IS NOT NULL " \ - " AND NOT (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ - " AND op_depth >= (SELECT MAX(op_depth) FROM nodes o " \ - " WHERE o.wc_id = ?1 " \ - " AND o.local_relpath = ?2) " \ - "" - #define STMT_SELECT_MOVED_PAIR3 192 #define STMT_192_INFO {"STMT_SELECT_MOVED_PAIR3", NULL} #define STMT_192 \ - "SELECT local_relpath, moved_to, op_depth, kind FROM nodes " \ - "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 " \ - " AND moved_to IS NOT NULL " \ + "SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind " \ + "FROM nodes n " \ + "JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath " \ + " AND d.op_depth = (SELECT MIN(dd.op_depth) " \ + " FROM nodes dd " \ + " WHERE dd.wc_id = ?1 " \ + " AND dd.local_relpath = d.local_relpath " \ + " AND dd.op_depth > ?3) " \ + "WHERE n.wc_id = ?1 AND n.local_relpath = ?2 AND n.op_depth = ?3 " \ + " AND d.moved_to IS NOT NULL " \ "UNION ALL " \ - "SELECT local_relpath, moved_to, op_depth, kind FROM nodes " \ - "WHERE wc_id = ?1 " \ - " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ - " AND op_depth > ?3 " \ - " AND moved_to IS NOT NULL " \ - "ORDER BY local_relpath, op_depth " \ + "SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind " \ + "FROM nodes n " \ + "JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath " \ + " AND d.op_depth = (SELECT MIN(dd.op_depth) " \ + " FROM nodes dd " \ + " WHERE dd.wc_id = ?1 " \ + " AND dd.local_relpath = d.local_relpath " \ + " AND dd.op_depth > ?3) " \ + "WHERE n.wc_id = ?1 AND (((n.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((n.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ + " AND n.op_depth = ?3 " \ + " AND d.moved_to IS NOT NULL " \ + "ORDER BY n.local_relpath " \ "" #define STMT_SELECT_MOVED_OUTSIDE 193 @@ -2039,44 +2116,27 @@ " AND NOT (((moved_to) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((moved_to) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ "" -#define STMT_SELECT_OP_DEPTH_MOVED_PAIR 194 -#define STMT_194_INFO {"STMT_SELECT_OP_DEPTH_MOVED_PAIR", NULL} +#define STMT_SELECT_MOVED_DESCENDANTS_SRC 194 +#define STMT_194_INFO {"STMT_SELECT_MOVED_DESCENDANTS_SRC", NULL} #define STMT_194 \ - "SELECT n.local_relpath, n.moved_to, " \ - " (SELECT o.repos_path FROM nodes AS o " \ - " WHERE o.wc_id = n.wc_id " \ - " AND o.local_relpath = n.local_relpath " \ - " AND o.op_depth < ?3 ORDER BY o.op_depth DESC LIMIT 1) " \ - "FROM nodes AS n " \ - "WHERE n.wc_id = ?1 " \ - " AND (((n.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((n.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ - " AND n.op_depth = ?3 " \ - " AND n.moved_to IS NOT NULL " \ - "" - -#define STMT_SELECT_MOVED_DESCENDANTS 195 -#define STMT_195_INFO {"STMT_SELECT_MOVED_DESCENDANTS", NULL} + "SELECT s.op_depth, n.local_relpath, n.kind, n.repos_path, s.moved_to " \ + "FROM nodes n " \ + "JOIN nodes s ON s.wc_id = n.wc_id AND s.local_relpath = n.local_relpath " \ + " AND s.op_depth = (SELECT MIN(d.op_depth) " \ + " FROM nodes d " \ + " WHERE d.wc_id = ?1 " \ + " AND d.local_relpath = s.local_relpath " \ + " AND d.op_depth > ?3) " \ + "WHERE n.wc_id = ?1 AND n.op_depth = ?3 " \ + " AND (n.local_relpath = ?2 OR (((n.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((n.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END))) " \ + " AND s.moved_to IS NOT NULL " \ + "" + +#define STMT_COMMIT_UPDATE_ORIGIN 195 +#define STMT_195_INFO {"STMT_COMMIT_UPDATE_ORIGIN", NULL} #define STMT_195 \ - "SELECT n.local_relpath, h.moved_to " \ - "FROM nodes n, nodes h " \ - "WHERE n.wc_id = ?1 " \ - " AND h.wc_id = ?1 " \ - " AND (((n.local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((n.local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ - " AND h.local_relpath = n.local_relpath " \ - " AND n.op_depth = ?3 " \ - " AND h.op_depth = (SELECT MIN(o.op_depth) " \ - " FROM nodes o " \ - " WHERE o.wc_id = ?1 " \ - " AND o.local_relpath = n.local_relpath " \ - " AND o.op_depth > ?3) " \ - " AND h.moved_to IS NOT NULL " \ - "" - -#define STMT_COMMIT_UPDATE_ORIGIN 196 -#define STMT_196_INFO {"STMT_COMMIT_UPDATE_ORIGIN", NULL} -#define STMT_196 \ "UPDATE nodes SET repos_id = ?4, " \ - " repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1), " \ + " repos_path = (CASE WHEN (?2) = '' THEN (CASE WHEN (?5) = '' THEN (local_relpath) WHEN (local_relpath) = '' THEN (?5) ELSE (?5) || '/' || (local_relpath) END) WHEN (?5) = '' THEN (CASE WHEN (?2) = '' THEN (local_relpath) WHEN SUBSTR((local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(local_relpath) THEN '' WHEN SUBSTR((local_relpath), LENGTH(?2)+1, 1) = '/' THEN SUBSTR((local_relpath), LENGTH(?2)+2) END END) WHEN SUBSTR((local_relpath), 1, LENGTH(?2)) = (?2) THEN CASE WHEN LENGTH(?2) = LENGTH(local_relpath) THEN (?5) WHEN SUBSTR((local_relpath), LENGTH(?2)+1, 1) = '/' THEN (?5) || SUBSTR((local_relpath), LENGTH(?2)+1) END END), " \ " revision = ?6 " \ "WHERE wc_id = ?1 " \ " AND (local_relpath = ?2 " \ @@ -2084,16 +2144,16 @@ " AND op_depth = ?3 " \ "" -#define STMT_HAS_LAYER_BETWEEN 197 -#define STMT_197_INFO {"STMT_HAS_LAYER_BETWEEN", NULL} -#define STMT_197 \ +#define STMT_HAS_LAYER_BETWEEN 196 +#define STMT_196_INFO {"STMT_HAS_LAYER_BETWEEN", NULL} +#define STMT_196 \ "SELECT 1 FROM NODES " \ "WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 AND op_depth < ?4 " \ "" -#define STMT_SELECT_REPOS_PATH_REVISION 198 -#define STMT_198_INFO {"STMT_SELECT_REPOS_PATH_REVISION", NULL} -#define STMT_198 \ +#define STMT_SELECT_REPOS_PATH_REVISION 197 +#define STMT_197_INFO {"STMT_SELECT_REPOS_PATH_REVISION", NULL} +#define STMT_197 \ "SELECT local_relpath, repos_path, revision FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ @@ -2101,50 +2161,52 @@ "ORDER BY local_relpath " \ "" -#define STMT_SELECT_HAS_NON_FILE_CHILDREN 199 -#define STMT_199_INFO {"STMT_SELECT_HAS_NON_FILE_CHILDREN", NULL} -#define STMT_199 \ +#define STMT_SELECT_HAS_NON_FILE_CHILDREN 198 +#define STMT_198_INFO {"STMT_SELECT_HAS_NON_FILE_CHILDREN", NULL} +#define STMT_198 \ "SELECT 1 FROM nodes " \ - "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 AND kind != 'file' " \ + "WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = ?3 AND kind != 'file' " \ + "LIMIT 1 " \ "" -#define STMT_SELECT_HAS_GRANDCHILDREN 200 -#define STMT_200_INFO {"STMT_SELECT_HAS_GRANDCHILDREN", NULL} -#define STMT_200 \ +#define STMT_SELECT_HAS_GRANDCHILDREN 199 +#define STMT_199_INFO {"STMT_SELECT_HAS_GRANDCHILDREN", NULL} +#define STMT_199 \ "SELECT 1 FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (((parent_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((parent_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ - " AND op_depth = 0 " \ + " AND op_depth = ?3 " \ " AND file_external IS NULL " \ + "LIMIT 1 " \ "" -#define STMT_SELECT_ALL_NODES 201 -#define STMT_201_INFO {"STMT_SELECT_ALL_NODES", NULL} -#define STMT_201 \ +#define STMT_SELECT_ALL_NODES 200 +#define STMT_200_INFO {"STMT_SELECT_ALL_NODES", NULL} +#define STMT_200 \ "SELECT op_depth, local_relpath, parent_relpath, file_external FROM nodes " \ "WHERE wc_id = ?1 " \ "" -#define STMT_SELECT_IPROPS 202 -#define STMT_202_INFO {"STMT_SELECT_IPROPS", NULL} -#define STMT_202 \ +#define STMT_SELECT_IPROPS 201 +#define STMT_201_INFO {"STMT_SELECT_IPROPS", NULL} +#define STMT_201 \ "SELECT inherited_props FROM nodes " \ "WHERE wc_id = ?1 " \ " AND local_relpath = ?2 " \ " AND op_depth = 0 " \ "" -#define STMT_UPDATE_IPROP 203 -#define STMT_203_INFO {"STMT_UPDATE_IPROP", NULL} -#define STMT_203 \ +#define STMT_UPDATE_IPROP 202 +#define STMT_202_INFO {"STMT_UPDATE_IPROP", NULL} +#define STMT_202 \ "UPDATE nodes " \ "SET inherited_props = ?3 " \ "WHERE (wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0) " \ "" -#define STMT_SELECT_IPROPS_NODE 204 -#define STMT_204_INFO {"STMT_SELECT_IPROPS_NODE", NULL} -#define STMT_204 \ +#define STMT_SELECT_IPROPS_NODE 203 +#define STMT_203_INFO {"STMT_SELECT_IPROPS_NODE", NULL} +#define STMT_203 \ "SELECT local_relpath, repos_path FROM nodes " \ "WHERE wc_id = ?1 " \ " AND local_relpath = ?2 " \ @@ -2152,9 +2214,9 @@ " AND (inherited_props not null) " \ "" -#define STMT_SELECT_IPROPS_RECURSIVE 205 -#define STMT_205_INFO {"STMT_SELECT_IPROPS_RECURSIVE", NULL} -#define STMT_205 \ +#define STMT_SELECT_IPROPS_RECURSIVE 204 +#define STMT_204_INFO {"STMT_SELECT_IPROPS_RECURSIVE", NULL} +#define STMT_204 \ "SELECT local_relpath, repos_path FROM nodes " \ "WHERE wc_id = ?1 " \ " AND (((local_relpath) > (CASE (?2) WHEN '' THEN '' ELSE (?2) || '/' END)) AND ((local_relpath) < CASE (?2) WHEN '' THEN X'FFFF' ELSE (?2) || '0' END)) " \ @@ -2162,9 +2224,9 @@ " AND (inherited_props not null) " \ "" -#define STMT_SELECT_IPROPS_CHILDREN 206 -#define STMT_206_INFO {"STMT_SELECT_IPROPS_CHILDREN", NULL} -#define STMT_206 \ +#define STMT_SELECT_IPROPS_CHILDREN 205 +#define STMT_205_INFO {"STMT_SELECT_IPROPS_CHILDREN", NULL} +#define STMT_205 \ "SELECT local_relpath, repos_path FROM nodes " \ "WHERE wc_id = ?1 " \ " AND parent_relpath = ?2 " \ @@ -2172,16 +2234,16 @@ " AND (inherited_props not null) " \ "" -#define STMT_HAVE_STAT1_TABLE 207 -#define STMT_207_INFO {"STMT_HAVE_STAT1_TABLE", NULL} -#define STMT_207 \ +#define STMT_HAVE_STAT1_TABLE 206 +#define STMT_206_INFO {"STMT_HAVE_STAT1_TABLE", NULL} +#define STMT_206 \ "SELECT 1 FROM sqlite_master WHERE name='sqlite_stat1' AND type='table' " \ "LIMIT 1 " \ "" -#define STMT_CREATE_SCHEMA 208 -#define STMT_208_INFO {"STMT_CREATE_SCHEMA", NULL} -#define STMT_208 \ +#define STMT_CREATE_SCHEMA 207 +#define STMT_207_INFO {"STMT_CREATE_SCHEMA", NULL} +#define STMT_207 \ "CREATE TABLE REPOSITORY ( " \ " id INTEGER PRIMARY KEY AUTOINCREMENT, " \ " root TEXT UNIQUE NOT NULL, " \ @@ -2246,9 +2308,9 @@ "; " \ "" -#define STMT_CREATE_NODES 209 -#define STMT_209_INFO {"STMT_CREATE_NODES", NULL} -#define STMT_209 \ +#define STMT_CREATE_NODES 208 +#define STMT_208_INFO {"STMT_CREATE_NODES", NULL} +#define STMT_208 \ "CREATE TABLE NODES ( " \ " wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \ " local_relpath TEXT NOT NULL, " \ @@ -2288,9 +2350,9 @@ " WHERE op_depth = 0; " \ "" -#define STMT_CREATE_NODES_TRIGGERS 210 -#define STMT_210_INFO {"STMT_CREATE_NODES_TRIGGERS", NULL} -#define STMT_210 \ +#define STMT_CREATE_NODES_TRIGGERS 209 +#define STMT_209_INFO {"STMT_CREATE_NODES_TRIGGERS", NULL} +#define STMT_209 \ "CREATE TRIGGER nodes_insert_trigger " \ "AFTER INSERT ON nodes " \ "WHEN NEW.checksum IS NOT NULL " \ @@ -2316,9 +2378,9 @@ "END; " \ "" -#define STMT_CREATE_EXTERNALS 211 -#define STMT_211_INFO {"STMT_CREATE_EXTERNALS", NULL} -#define STMT_211 \ +#define STMT_CREATE_EXTERNALS 210 +#define STMT_210_INFO {"STMT_CREATE_EXTERNALS", NULL} +#define STMT_210 \ "CREATE TABLE EXTERNALS ( " \ " wc_id INTEGER NOT NULL REFERENCES WCROOT (id), " \ " local_relpath TEXT NOT NULL, " \ @@ -2337,32 +2399,36 @@ " local_relpath); " \ "" -#define STMT_INSTALL_SCHEMA_STATISTICS 212 -#define STMT_212_INFO {"STMT_INSTALL_SCHEMA_STATISTICS", NULL} -#define STMT_212 \ +#define STMT_INSTALL_SCHEMA_STATISTICS 211 +#define STMT_211_INFO {"STMT_INSTALL_SCHEMA_STATISTICS", NULL} +#define STMT_211 \ "ANALYZE sqlite_master; " \ "DELETE FROM sqlite_stat1 " \ - "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "WHERE tbl in ('NODES', 'ACTUAL_NODE', 'LOCK', 'WC_LOCK', 'EXTERNALS'); " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('NODES', 'sqlite_autoindex_NODES_1', '8000 8000 2 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('NODES', 'I_NODES_PARENT', '8000 8000 10 2 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('NODES', 'I_NODES_MOVED', '8000 8000 1 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('ACTUAL_NODE', 'sqlite_autoindex_ACTUAL_NODE_1', '8000 8000 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('ACTUAL_NODE', 'I_ACTUAL_PARENT', '8000 8000 10 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('LOCK', 'sqlite_autoindex_LOCK_1', '100 100 1'); " \ - "INSERT OR REPLACE INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ " ('WC_LOCK', 'sqlite_autoindex_WC_LOCK_1', '100 100 1'); " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + " ('EXTERNALS','sqlite_autoindex_EXTERNALS_1', '100 100 1'); " \ + "INSERT INTO sqlite_stat1(tbl, idx, stat) VALUES " \ + " ('EXTERNALS','I_EXTERNALS_DEFINED', '100 100 3 1'); " \ "ANALYZE sqlite_master; " \ "" -#define STMT_UPGRADE_TO_20 213 -#define STMT_213_INFO {"STMT_UPGRADE_TO_20", NULL} -#define STMT_213 \ +#define STMT_UPGRADE_TO_20 212 +#define STMT_212_INFO {"STMT_UPGRADE_TO_20", NULL} +#define STMT_212 \ "UPDATE BASE_NODE SET checksum = (SELECT checksum FROM pristine " \ " WHERE md5_checksum = BASE_NODE.checksum) " \ "WHERE EXISTS (SELECT 1 FROM pristine WHERE md5_checksum = BASE_NODE.checksum); " \ @@ -2403,59 +2469,59 @@ "PRAGMA user_version = 20; " \ "" -#define STMT_UPGRADE_TO_21 214 -#define STMT_214_INFO {"STMT_UPGRADE_TO_21", NULL} -#define STMT_214 \ +#define STMT_UPGRADE_TO_21 213 +#define STMT_213_INFO {"STMT_UPGRADE_TO_21", NULL} +#define STMT_213 \ "PRAGMA user_version = 21; " \ "" -#define STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT 215 -#define STMT_215_INFO {"STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT", NULL} -#define STMT_215 \ +#define STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT 214 +#define STMT_214_INFO {"STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT", NULL} +#define STMT_214 \ "SELECT wc_id, local_relpath, tree_conflict_data " \ "FROM actual_node " \ "WHERE tree_conflict_data IS NOT NULL " \ "" -#define STMT_UPGRADE_21_ERASE_OLD_CONFLICTS 216 -#define STMT_216_INFO {"STMT_UPGRADE_21_ERASE_OLD_CONFLICTS", NULL} -#define STMT_216 \ +#define STMT_UPGRADE_21_ERASE_OLD_CONFLICTS 215 +#define STMT_215_INFO {"STMT_UPGRADE_21_ERASE_OLD_CONFLICTS", NULL} +#define STMT_215 \ "UPDATE actual_node SET tree_conflict_data = NULL " \ "" -#define STMT_UPGRADE_TO_22 217 -#define STMT_217_INFO {"STMT_UPGRADE_TO_22", NULL} -#define STMT_217 \ +#define STMT_UPGRADE_TO_22 216 +#define STMT_216_INFO {"STMT_UPGRADE_TO_22", NULL} +#define STMT_216 \ "UPDATE actual_node SET tree_conflict_data = conflict_data; " \ "UPDATE actual_node SET conflict_data = NULL; " \ "PRAGMA user_version = 22; " \ "" -#define STMT_UPGRADE_TO_23 218 -#define STMT_218_INFO {"STMT_UPGRADE_TO_23", NULL} -#define STMT_218 \ +#define STMT_UPGRADE_TO_23 217 +#define STMT_217_INFO {"STMT_UPGRADE_TO_23", NULL} +#define STMT_217 \ "PRAGMA user_version = 23; " \ "" -#define STMT_UPGRADE_23_HAS_WORKING_NODES 219 -#define STMT_219_INFO {"STMT_UPGRADE_23_HAS_WORKING_NODES", NULL} -#define STMT_219 \ +#define STMT_UPGRADE_23_HAS_WORKING_NODES 218 +#define STMT_218_INFO {"STMT_UPGRADE_23_HAS_WORKING_NODES", NULL} +#define STMT_218 \ "SELECT 1 FROM nodes WHERE op_depth > 0 " \ "LIMIT 1 " \ "" -#define STMT_UPGRADE_TO_24 220 -#define STMT_220_INFO {"STMT_UPGRADE_TO_24", NULL} -#define STMT_220 \ +#define STMT_UPGRADE_TO_24 219 +#define STMT_219_INFO {"STMT_UPGRADE_TO_24", NULL} +#define STMT_219 \ "UPDATE pristine SET refcount = " \ " (SELECT COUNT(*) FROM nodes " \ " WHERE checksum = pristine.checksum ); " \ "PRAGMA user_version = 24; " \ "" -#define STMT_UPGRADE_TO_25 221 -#define STMT_221_INFO {"STMT_UPGRADE_TO_25", NULL} -#define STMT_221 \ +#define STMT_UPGRADE_TO_25 220 +#define STMT_220_INFO {"STMT_UPGRADE_TO_25", NULL} +#define STMT_220 \ "DROP VIEW IF EXISTS NODES_CURRENT; " \ "CREATE VIEW NODES_CURRENT AS " \ " SELECT * FROM nodes " \ @@ -2467,9 +2533,9 @@ "PRAGMA user_version = 25; " \ "" -#define STMT_UPGRADE_TO_26 222 -#define STMT_222_INFO {"STMT_UPGRADE_TO_26", NULL} -#define STMT_222 \ +#define STMT_UPGRADE_TO_26 221 +#define STMT_221_INFO {"STMT_UPGRADE_TO_26", NULL} +#define STMT_221 \ "DROP VIEW IF EXISTS NODES_BASE; " \ "CREATE VIEW NODES_BASE AS " \ " SELECT * FROM nodes " \ @@ -2477,15 +2543,15 @@ "PRAGMA user_version = 26; " \ "" -#define STMT_UPGRADE_TO_27 223 -#define STMT_223_INFO {"STMT_UPGRADE_TO_27", NULL} -#define STMT_223 \ +#define STMT_UPGRADE_TO_27 222 +#define STMT_222_INFO {"STMT_UPGRADE_TO_27", NULL} +#define STMT_222 \ "PRAGMA user_version = 27; " \ "" -#define STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS 224 -#define STMT_224_INFO {"STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS", NULL} -#define STMT_224 \ +#define STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS 223 +#define STMT_223_INFO {"STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS", NULL} +#define STMT_223 \ "SELECT 1 FROM actual_node " \ "WHERE NOT ((prop_reject IS NULL) AND (conflict_old IS NULL) " \ " AND (conflict_new IS NULL) AND (conflict_working IS NULL) " \ @@ -2493,18 +2559,18 @@ "LIMIT 1 " \ "" -#define STMT_UPGRADE_TO_28 225 -#define STMT_225_INFO {"STMT_UPGRADE_TO_28", NULL} -#define STMT_225 \ +#define STMT_UPGRADE_TO_28 224 +#define STMT_224_INFO {"STMT_UPGRADE_TO_28", NULL} +#define STMT_224 \ "UPDATE NODES SET checksum = (SELECT checksum FROM pristine " \ " WHERE md5_checksum = nodes.checksum) " \ "WHERE EXISTS (SELECT 1 FROM pristine WHERE md5_checksum = nodes.checksum); " \ "PRAGMA user_version = 28; " \ "" -#define STMT_UPGRADE_TO_29 226 -#define STMT_226_INFO {"STMT_UPGRADE_TO_29", NULL} -#define STMT_226 \ +#define STMT_UPGRADE_TO_29 225 +#define STMT_225_INFO {"STMT_UPGRADE_TO_29", NULL} +#define STMT_225 \ "DROP TRIGGER IF EXISTS nodes_update_checksum_trigger; " \ "DROP TRIGGER IF EXISTS nodes_insert_trigger; " \ "DROP TRIGGER IF EXISTS nodes_delete_trigger; " \ @@ -2534,9 +2600,9 @@ "PRAGMA user_version = 29; " \ "" -#define STMT_UPGRADE_TO_30 227 -#define STMT_227_INFO {"STMT_UPGRADE_TO_30", NULL} -#define STMT_227 \ +#define STMT_UPGRADE_TO_30 226 +#define STMT_226_INFO {"STMT_UPGRADE_TO_30", NULL} +#define STMT_226 \ "CREATE UNIQUE INDEX IF NOT EXISTS I_NODES_MOVED " \ "ON NODES (wc_id, moved_to, op_depth); " \ "CREATE INDEX IF NOT EXISTS I_PRISTINE_MD5 ON PRISTINE (md5_checksum); " \ @@ -2544,9 +2610,9 @@ "UPDATE nodes SET file_external=1 WHERE file_external IS NOT NULL; " \ "" -#define STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE 228 -#define STMT_228_INFO {"STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE", NULL} -#define STMT_228 \ +#define STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE 227 +#define STMT_227_INFO {"STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE", NULL} +#define STMT_227 \ "SELECT wc_id, local_relpath, " \ " conflict_old, conflict_working, conflict_new, prop_reject, tree_conflict_data " \ "FROM actual_node " \ @@ -2558,24 +2624,24 @@ "ORDER by wc_id, local_relpath " \ "" -#define STMT_UPGRADE_30_SET_CONFLICT 229 -#define STMT_229_INFO {"STMT_UPGRADE_30_SET_CONFLICT", NULL} -#define STMT_229 \ +#define STMT_UPGRADE_30_SET_CONFLICT 228 +#define STMT_228_INFO {"STMT_UPGRADE_30_SET_CONFLICT", NULL} +#define STMT_228 \ "UPDATE actual_node SET conflict_data = ?3, conflict_old = NULL, " \ " conflict_working = NULL, conflict_new = NULL, prop_reject = NULL, " \ " tree_conflict_data = NULL " \ "WHERE wc_id = ?1 and local_relpath = ?2 " \ "" -#define STMT_UPGRADE_TO_31_ALTER_TABLE 230 -#define STMT_230_INFO {"STMT_UPGRADE_TO_31_ALTER_TABLE", NULL} -#define STMT_230 \ +#define STMT_UPGRADE_TO_31_ALTER_TABLE 229 +#define STMT_229_INFO {"STMT_UPGRADE_TO_31_ALTER_TABLE", NULL} +#define STMT_229 \ "ALTER TABLE NODES ADD COLUMN inherited_props BLOB; " \ "" -#define STMT_UPGRADE_TO_31_FINALIZE 231 -#define STMT_231_INFO {"STMT_UPGRADE_TO_31_FINALIZE", NULL} -#define STMT_231 \ +#define STMT_UPGRADE_TO_31_FINALIZE 230 +#define STMT_230_INFO {"STMT_UPGRADE_TO_31_FINALIZE", NULL} +#define STMT_230 \ "DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST; " \ "DROP INDEX IF EXISTS I_EXTERNALS_PARENT; " \ "DROP INDEX I_NODES_PARENT; " \ @@ -2587,9 +2653,9 @@ "PRAGMA user_version = 31; " \ "" -#define STMT_UPGRADE_31_SELECT_WCROOT_NODES 232 -#define STMT_232_INFO {"STMT_UPGRADE_31_SELECT_WCROOT_NODES", NULL} -#define STMT_232 \ +#define STMT_UPGRADE_31_SELECT_WCROOT_NODES 231 +#define STMT_231_INFO {"STMT_UPGRADE_31_SELECT_WCROOT_NODES", NULL} +#define STMT_231 \ "SELECT l.wc_id, l.local_relpath FROM nodes as l " \ "LEFT OUTER JOIN nodes as r " \ "ON l.wc_id = r.wc_id " \ @@ -2601,9 +2667,9 @@ " OR (l.repos_path IS NOT (CASE WHEN (r.local_relpath) = '' THEN (CASE WHEN (r.repos_path) = '' THEN (l.local_relpath) WHEN (l.local_relpath) = '' THEN (r.repos_path) ELSE (r.repos_path) || '/' || (l.local_relpath) END) WHEN (r.repos_path) = '' THEN (CASE WHEN (r.local_relpath) = '' THEN (l.local_relpath) WHEN SUBSTR((l.local_relpath), 1, LENGTH(r.local_relpath)) = (r.local_relpath) THEN CASE WHEN LENGTH(r.local_relpath) = LENGTH(l.local_relpath) THEN '' WHEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1, 1) = '/' THEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+2) END END) WHEN SUBSTR((l.local_relpath), 1, LENGTH(r.local_relpath)) = (r.local_relpath) THEN CASE WHEN LENGTH(r.local_relpath) = LENGTH(l.local_relpath) THEN (r.repos_path) WHEN SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1, 1) = '/' THEN (r.repos_path) || SUBSTR((l.local_relpath), LENGTH(r.local_relpath)+1) END END))) " \ "" -#define STMT_UPGRADE_TO_32 233 -#define STMT_233_INFO {"STMT_UPGRADE_TO_32", NULL} -#define STMT_233 \ +#define STMT_UPGRADE_TO_32 232 +#define STMT_232_INFO {"STMT_UPGRADE_TO_32", NULL} +#define STMT_232 \ "DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST; " \ "DROP INDEX IF EXISTS I_EXTERNALS_PARENT; " \ "CREATE INDEX I_EXTERNALS_PARENT ON EXTERNALS (wc_id, parent_relpath); " \ @@ -2656,9 +2722,9 @@ "DROP TABLE ACTUAL_NODE_BACKUP; " \ "" -#define STMT_VERIFICATION_TRIGGERS 234 -#define STMT_234_INFO {"STMT_VERIFICATION_TRIGGERS", NULL} -#define STMT_234 \ +#define STMT_VERIFICATION_TRIGGERS 233 +#define STMT_233_INFO {"STMT_VERIFICATION_TRIGGERS", NULL} +#define STMT_233 \ "CREATE TEMPORARY TRIGGER no_repository_updates BEFORE UPDATE ON repository " \ "BEGIN " \ " SELECT RAISE(FAIL, 'Updates to REPOSITORY are not allowed.'); " \ @@ -2697,6 +2763,179 @@ "END; " \ "" +#define STMT_STATIC_VERIFY 234 +#define STMT_234_INFO {"STMT_STATIC_VERIFY", NULL} +#define STMT_234 \ + "SELECT local_relpath, op_depth, 1, 'Invalid parent relpath set in NODES' " \ + "FROM nodes n WHERE local_relpath != '' " \ + " AND (parent_relpath IS NULL " \ + " OR NOT (((local_relpath) > (CASE (parent_relpath) WHEN '' THEN '' ELSE (parent_relpath) || '/' END)) AND ((local_relpath) < CASE (parent_relpath) WHEN '' THEN X'FFFF' ELSE (parent_relpath) || '0' END)) " \ + " OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) " \ + "UNION ALL " \ + "SELECT local_relpath, -1, 2, 'Invalid parent relpath set in ACTUAL' " \ + "FROM actual_node a WHERE local_relpath != '' " \ + " AND (parent_relpath IS NULL " \ + " OR NOT (((local_relpath) > (CASE (parent_relpath) WHEN '' THEN '' ELSE (parent_relpath) || '/' END)) AND ((local_relpath) < CASE (parent_relpath) WHEN '' THEN X'FFFF' ELSE (parent_relpath) || '0' END)) " \ + " OR relpath_depth(local_relpath) != relpath_depth(parent_relpath)+1) " \ + "UNION ALL " \ + "SELECT local_relpath, -1, 10, 'No ancestor in ACTUAL' " \ + "FROM actual_node a WHERE local_relpath != '' " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.parent_relpath) " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.local_relpath) " \ + "UNION ALL " \ + "SELECT a.local_relpath, -1, 11, 'Bad or Unneeded actual data' " \ + "FROM actual_node a " \ + "LEFT JOIN nodes n on n.wc_id = a.wc_id AND n.local_relpath = a.local_relpath " \ + " AND n.op_depth = (SELECT MAX(op_depth) from nodes i " \ + " WHERE i.wc_id=a.wc_id AND i.local_relpath=a.local_relpath) " \ + "WHERE (a.properties IS NOT NULL " \ + " AND (n.presence IS NULL " \ + " OR n.presence NOT IN ('normal', 'incomplete'))) " \ + " OR (a.changelist IS NOT NULL AND (n.kind IS NOT NULL AND n.kind != 'file')) " \ + " OR (a.conflict_data IS NULL AND a.properties IS NULL AND a.changelist IS NULL) " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=a.wc_id " \ + " AND i.local_relpath=a.parent_relpath) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 20, 'No ancestor in NODES' " \ + "FROM nodes n WHERE local_relpath != '' " \ + " AND file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 from nodes i " \ + " WHERE i.wc_id=n.wc_id " \ + " AND i.local_relpath=n.parent_relpath " \ + " AND i.op_depth <= n.op_depth) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 21, 'Unneeded node data' " \ + "FROM nodes " \ + "WHERE presence NOT IN ('normal', 'incomplete') " \ + "AND (properties IS NOT NULL " \ + " OR checksum IS NOT NULL " \ + " OR depth IS NOT NULL " \ + " OR symlink_target IS NOT NULL " \ + " OR changed_revision IS NOT NULL " \ + " OR (changed_date IS NOT NULL AND changed_date != 0) " \ + " OR changed_author IS NOT NULL " \ + " OR translated_size IS NOT NULL " \ + " OR last_mod_time IS NOT NULL " \ + " OR dav_cache IS NOT NULL " \ + " OR file_external IS NOT NULL " \ + " OR inherited_props IS NOT NULL) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 22, 'Unneeded base-deleted node data' " \ + "FROM nodes " \ + "WHERE presence IN ('base-deleted') " \ + "AND (repos_id IS NOT NULL " \ + " OR repos_path IS NOT NULL " \ + " OR revision IS NOT NULL) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 23, 'Kind specific data invalid on normal' " \ + "FROM nodes " \ + "WHERE presence IN ('normal', 'incomplete') " \ + "AND (kind IS NULL " \ + " OR (repos_path IS NULL " \ + " AND (properties IS NOT NULL " \ + " OR changed_revision IS NOT NULL " \ + " OR changed_author IS NOT NULL " \ + " OR (changed_date IS NOT NULL AND changed_date != 0))) " \ + " OR (CASE WHEN kind = 'file' AND repos_path IS NOT NULL " \ + " THEN checksum IS NULL " \ + " ELSE checksum IS NOT NULL END) " \ + " OR (CASE WHEN kind = 'dir' THEN depth IS NULL " \ + " ELSE depth IS NOT NULL END) " \ + " OR (CASE WHEN kind = 'symlink' THEN symlink_target IS NULL " \ + " ELSE symlink_target IS NOT NULL END)) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 24, 'Invalid op-depth for local add' " \ + "FROM nodes " \ + "WHERE presence IN ('normal', 'incomplete') " \ + " AND repos_path IS NULL " \ + " AND op_depth != relpath_depth(local_relpath) " \ + "UNION ALL " \ + "SELECT local_relpath, op_depth, 25, 'Node missing op-depth ancestor' " \ + "FROM nodes n " \ + "WHERE op_depth < relpath_depth(local_relpath) " \ + " AND file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes p " \ + " WHERE p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath " \ + " AND p.op_depth=n.op_depth " \ + " AND (p.presence IN ('normal', 'incomplete') " \ + " OR (p.presence IN ('base-deleted', 'not-present') " \ + " AND n.presence = 'base-deleted'))) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 26, 'Copied descendant mismatch' " \ + "FROM nodes n " \ + "JOIN nodes p " \ + " ON p.wc_id=n.wc_id AND p.local_relpath=n.parent_relpath " \ + " AND n.op_depth=p.op_depth " \ + "WHERE n.op_depth > 0 AND n.presence IN ('normal', 'incomplete') " \ + " AND (n.repos_id != p.repos_id " \ + " OR n.repos_path != " \ + " (CASE WHEN (n.parent_relpath) = '' THEN (CASE WHEN (p.repos_path) = '' THEN (n.local_relpath) WHEN (n.local_relpath) = '' THEN (p.repos_path) ELSE (p.repos_path) || '/' || (n.local_relpath) END) WHEN (p.repos_path) = '' THEN (CASE WHEN (n.parent_relpath) = '' THEN (n.local_relpath) WHEN SUBSTR((n.local_relpath), 1, LENGTH(n.parent_relpath)) = (n.parent_relpath) THEN CASE WHEN LENGTH(n.parent_relpath) = LENGTH(n.local_relpath) THEN '' WHEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1, 1) = '/' THEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+2) END END) WHEN SUBSTR((n.local_relpath), 1, LENGTH(n.parent_relpath)) = (n.parent_relpath) THEN CASE WHEN LENGTH(n.parent_relpath) = LENGTH(n.local_relpath) THEN (p.repos_path) WHEN SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1, 1) = '/' THEN (p.repos_path) || SUBSTR((n.local_relpath), LENGTH(n.parent_relpath)+1) END END) " \ + " OR n.revision != p.revision " \ + " OR p.kind != 'dir' " \ + " OR n.moved_here IS NOT p.moved_here) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 27, 'Invalid op-root presence' " \ + "FROM nodes n " \ + "WHERE n.op_depth = relpath_depth(local_relpath) " \ + " AND presence NOT IN ('normal', 'incomplete', 'base-deleted') " \ + "UNION ALL " \ + "SELECT n.local_relpath, s.op_depth, 28, 'Incomplete shadowing' " \ + "FROM nodes n " \ + "JOIN nodes s ON s.wc_id=n.wc_id AND s.local_relpath=n.local_relpath " \ + " AND s.op_depth = relpath_depth(s.local_relpath) " \ + " AND s.op_depth = (SELECT MIN(op_depth) FROM nodes d " \ + " WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath " \ + " AND d.op_depth > n.op_depth) " \ + "WHERE n.presence IN ('normal', 'incomplete') " \ + " AND EXISTS(SELECT 1 " \ + " FROM nodes dn " \ + " WHERE dn.wc_id=n.wc_id AND dn.op_depth=n.op_depth " \ + " AND dn.presence IN ('normal', 'incomplete') " \ + " AND (((dn.local_relpath) > (CASE (n.local_relpath) WHEN '' THEN '' ELSE (n.local_relpath) || '/' END)) AND ((dn.local_relpath) < CASE (n.local_relpath) WHEN '' THEN X'FFFF' ELSE (n.local_relpath) || '0' END)) " \ + " AND dn.file_external IS NULL " \ + " AND NOT EXISTS(SELECT 1 " \ + " FROM nodes ds " \ + " WHERE ds.wc_id=n.wc_id AND ds.op_depth=s.op_depth " \ + " AND ds.local_relpath=dn.local_relpath)) " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.op_depth, 29, 'Invalid base-delete' " \ + "FROM nodes s " \ + "LEFT JOIN nodes n ON n.wc_id=s.wc_id AND n.local_relpath=s.local_relpath " \ + " AND n.op_depth = (SELECT MAX(op_depth) FROM nodes d " \ + " WHERE d.wc_id=s.wc_id AND d.local_relpath=s.local_relpath " \ + " AND d.op_depth < s.op_depth) " \ + "WHERE s.presence = 'base-deleted' " \ + " AND (n.presence IS NULL " \ + " OR n.presence NOT IN ('normal', 'incomplete') " \ + " ) " \ + "UNION ALL " \ + "SELECT n.local_relpath, n.op_depth, 30, 'Invalid data for BASE' " \ + "FROM nodes n " \ + "WHERE n.op_depth = 0 " \ + " AND (n.moved_to IS NOT NULL " \ + " OR n.moved_here IS NOT NULL) " \ + "UNION ALL " \ + "SELECT d.local_relpath, d.op_depth, 60, 'Moved here without origin' " \ + "FROM nodes d " \ + "WHERE d.op_depth = relpath_depth(d.local_relpath) " \ + " AND d.moved_here IS NOT NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes s " \ + " WHERE s.wc_id = d.wc_id AND s.moved_to = d.local_relpath) " \ + "UNION ALL " \ + "SELECT s.local_relpath, s.op_depth, 61, 'Moved to without target' " \ + "FROM nodes s " \ + "WHERE s.moved_to IS NOT NULL " \ + " AND NOT EXISTS(SELECT 1 FROM nodes d " \ + " WHERE d.wc_id = s.wc_id AND d.local_relpath = s.moved_to " \ + " AND d.op_depth = relpath_depth(d.local_relpath) " \ + " AND d.moved_here =1 AND d.repos_path IS NOT NULL) " \ + "" + #define WC_QUERIES_SQL_DECLARE_STATEMENTS(varname) \ static const char * const varname[] = { \ STMT_0, \ diff --git a/subversion/libsvn_wc/wc-queries.sql b/subversion/libsvn_wc/wc-queries.sql index 7cdb46c..3a8bf92 100644 --- a/subversion/libsvn_wc/wc-queries.sql +++ b/subversion/libsvn_wc/wc-queries.sql @@ -72,6 +72,12 @@ WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 -- STMT_SELECT_BASE_CHILDREN_INFO SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind, + revision, depth, file_external +FROM nodes +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 + +-- STMT_SELECT_BASE_CHILDREN_INFO_LOCK +SELECT local_relpath, nodes.repos_id, nodes.repos_path, presence, kind, revision, depth, file_external, lock_token, lock_owner, lock_comment, lock_date FROM nodes @@ -79,6 +85,7 @@ LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id AND nodes.repos_path = lock.repos_relpath WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 + -- STMT_SELECT_WORKING_NODE SELECT op_depth, presence, kind, checksum, translated_size, changed_revision, changed_date, changed_author, depth, symlink_target, @@ -92,7 +99,7 @@ LIMIT 1 -- STMT_SELECT_DEPTH_NODE SELECT repos_id, repos_path, presence, kind, revision, checksum, translated_size, changed_revision, changed_date, changed_author, depth, - symlink_target, last_mod_time, properties + symlink_target, properties, moved_to, moved_here FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 @@ -118,8 +125,9 @@ WHERE wc_id = ?1 AND local_relpath = ?2 -- STMT_SELECT_NODE_CHILDREN_INFO /* Getting rows in an advantageous order using ORDER BY local_relpath, op_depth DESC - turns out to be slower than getting rows in a random order and making the - C code handle it. */ + doesn't work as the index is created without the DESC keyword. + Using both local_relpath and op_depth descending does work without any + performance penalty. */ SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision, checksum, translated_size, changed_revision, changed_date, changed_author, depth, symlink_target, last_mod_time, properties, lock_token, lock_owner, @@ -128,11 +136,27 @@ FROM nodes LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id AND nodes.repos_path = lock.repos_relpath AND op_depth = 0 WHERE wc_id = ?1 AND parent_relpath = ?2 +ORDER BY local_relpath DESC, op_depth DESC + +-- STMT_SELECT_BASE_NODE_CHILDREN_INFO +/* See above re: result ordering. The results of this query must be in +the same order as returned by STMT_SELECT_NODE_CHILDREN_INFO, because +read_children_info expects them to be. */ +SELECT op_depth, nodes.repos_id, nodes.repos_path, presence, kind, revision, + checksum, translated_size, changed_revision, changed_date, changed_author, + depth, symlink_target, last_mod_time, properties, lock_token, lock_owner, + lock_comment, lock_date, local_relpath, moved_here, moved_to, file_external +FROM nodes +LEFT OUTER JOIN lock ON nodes.repos_id = lock.repos_id + AND nodes.repos_path = lock.repos_relpath AND op_depth = 0 +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 +ORDER BY local_relpath DESC -- STMT_SELECT_NODE_CHILDREN_WALKER_INFO SELECT local_relpath, op_depth, presence, kind FROM nodes_current WHERE wc_id = ?1 AND parent_relpath = ?2 +ORDER BY local_relpath -- STMT_SELECT_ACTUAL_CHILDREN_INFO SELECT local_relpath, changelist, properties, conflict_data @@ -161,16 +185,6 @@ INSERT OR REPLACE INTO nodes ( VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23) --- STMT_SELECT_BASE_PRESENT -SELECT local_relpath, kind FROM nodes n -WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) - AND op_depth = 0 - AND presence in (MAP_NORMAL, MAP_INCOMPLETE) - AND NOT EXISTS(SELECT 1 FROM NODES w - WHERE w.wc_id = ?1 AND w.local_relpath = n.local_relpath - AND op_depth > 0) -ORDER BY local_relpath DESC - -- STMT_SELECT_WORKING_PRESENT SELECT local_relpath, kind, checksum, translated_size, last_mod_time FROM nodes n @@ -210,13 +224,23 @@ WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) -- STMT_DELETE_WORKING_BASE_DELETE DELETE FROM nodes +WHERE wc_id = ?1 AND local_relpath = ?2 + AND presence = MAP_BASE_DELETED + AND op_depth > ?3 + AND op_depth = (SELECT MIN(op_depth) FROM nodes n + WHERE n.wc_id = ?1 + AND n.local_relpath = nodes.local_relpath + AND op_depth > ?3) + +-- STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE +DELETE FROM nodes WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND presence = MAP_BASE_DELETED - AND op_depth > 0 + AND op_depth > ?3 AND op_depth = (SELECT MIN(op_depth) FROM nodes n WHERE n.wc_id = ?1 AND n.local_relpath = nodes.local_relpath - AND op_depth > 0) + AND op_depth > ?3) -- STMT_DELETE_WORKING_RECURSIVE DELETE FROM nodes @@ -225,34 +249,42 @@ WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) -- STMT_DELETE_BASE_RECURSIVE DELETE FROM nodes -WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) +WHERE wc_id = ?1 AND (local_relpath = ?2 + OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) AND op_depth = 0 -- STMT_DELETE_WORKING_OP_DEPTH DELETE FROM nodes -WHERE wc_id = ?1 - AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) - AND op_depth = ?3 - --- STMT_DELETE_WORKING_OP_DEPTH_ABOVE -DELETE FROM nodes -WHERE wc_id = ?1 - AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) - AND op_depth > ?3 - --- STMT_SELECT_LOCAL_RELPATH_OP_DEPTH -SELECT local_relpath -FROM nodes WHERE wc_id = ?1 AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) AND op_depth = ?3 --- STMT_SELECT_CHILDREN_OP_DEPTH +/* Full layer replacement check code for handling moves +The op_root must exist (or there is no layer to replace) and an op-root + always has presence 'normal' */ +-- STMT_SELECT_LAYER_FOR_REPLACE +SELECT s.local_relpath, s.kind, + RELPATH_SKIP_JOIN(?2, ?4, s.local_relpath) drp, 'normal' +FROM nodes s +WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3 +UNION ALL +SELECT s.local_relpath, s.kind, + RELPATH_SKIP_JOIN(?2, ?4, s.local_relpath) drp, d.presence +FROM nodes s +LEFT OUTER JOIN nodes d ON d.wc_id= ?1 AND d.op_depth = ?5 + AND d.local_relpath = drp +WHERE s.wc_id = ?1 + AND IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2) + AND s.op_depth = ?3 +ORDER BY s.local_relpath + +-- STMT_SELECT_DESCENDANTS_OP_DEPTH_RV SELECT local_relpath, kind FROM nodes WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND op_depth = ?3 + AND presence in (MAP_NORMAL, MAP_INCOMPLETE) ORDER BY local_relpath DESC -- STMT_COPY_NODE_MOVE @@ -262,25 +294,51 @@ INSERT OR REPLACE INTO nodes ( changed_author, checksum, properties, translated_size, last_mod_time, symlink_target, moved_here, moved_to ) SELECT - wc_id, ?4 /*local_relpath */, ?5 /*op_depth*/, ?6 /* parent_relpath */, - repos_id, - repos_path, revision, presence, depth, kind, changed_revision, - changed_date, changed_author, checksum, properties, translated_size, - last_mod_time, symlink_target, 1, - (SELECT dst.moved_to FROM nodes AS dst - WHERE dst.wc_id = ?1 - AND dst.local_relpath = ?4 - AND dst.op_depth = ?5) -FROM nodes -WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 + s.wc_id, ?4 /*local_relpath */, ?5 /*op_depth*/, ?6 /* parent_relpath */, + s.repos_id, + s.repos_path, s.revision, s.presence, s.depth, s.kind, s.changed_revision, + s.changed_date, s.changed_author, s.checksum, s.properties, + CASE WHEN d.checksum=s.checksum THEN d.translated_size END, + CASE WHEN d.checksum=s.checksum THEN d.last_mod_time END, + s.symlink_target, 1, d.moved_to +FROM nodes s +LEFT JOIN nodes d ON d.wc_id=?1 AND d.local_relpath=?4 AND d.op_depth=?5 +WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3 + +-- STMT_SELECT_NO_LONGER_MOVED_RV +SELECT d.local_relpath, RELPATH_SKIP_JOIN(?2, ?4, d.local_relpath) srp, + b.presence, b.op_depth +FROM nodes d +LEFT OUTER JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = d.local_relpath + AND b.op_depth = (SELECT MAX(x.op_depth) FROM nodes x + WHERE x.wc_id = ?1 + AND x.local_relpath = b.local_relpath + AND x.op_depth < ?3) +WHERE d.wc_id = ?1 + AND IS_STRICT_DESCENDANT_OF(d.local_relpath, ?2) + AND d.op_depth = ?3 + AND NOT EXISTS(SELECT * FROM nodes s + WHERE s.wc_id = ?1 + AND s.local_relpath = srp + AND s.op_depth = ?5) +ORDER BY d.local_relpath DESC -- STMT_SELECT_OP_DEPTH_CHILDREN SELECT local_relpath, kind FROM nodes -WHERE wc_id = ?1 +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = ?3 AND presence != MAP_BASE_DELETED AND file_external is NULL +ORDER BY local_relpath + +-- STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS +SELECT local_relpath, kind FROM nodes +WHERE wc_id = ?1 + AND parent_relpath = ?2 + AND op_depth = ?3 + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE) +ORDER BY local_relpath /* Used by non-recursive revert to detect higher level children, and actual-only rows that would be left orphans, if the revert @@ -288,7 +346,8 @@ WHERE wc_id = ?1 -- STMT_SELECT_GE_OP_DEPTH_CHILDREN SELECT 1 FROM nodes WHERE wc_id = ?1 AND parent_relpath = ?2 - AND (op_depth > ?3 OR (op_depth = ?3 AND presence != MAP_BASE_DELETED)) + AND (op_depth > ?3 OR (op_depth = ?3 + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE))) UNION ALL SELECT 1 FROM ACTUAL_NODE a WHERE wc_id = ?1 AND parent_relpath = ?2 @@ -318,10 +377,11 @@ WHERE wc_id = ?1 AND op_depth = ?3 -- STMT_COMMIT_DESCENDANTS_TO_BASE UPDATE NODES SET op_depth = 0, repos_id = ?4, - repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1), + repos_path = RELPATH_SKIP_JOIN(?2, ?5, local_relpath), revision = ?6, dav_cache = NULL, moved_here = NULL, + moved_to = NULL, presence = CASE presence WHEN MAP_NORMAL THEN MAP_NORMAL WHEN MAP_EXCLUDED THEN MAP_EXCLUDED @@ -334,22 +394,30 @@ WHERE wc_id = ?1 -- STMT_SELECT_NODE_CHILDREN /* Return all paths that are children of the directory (?1, ?2) in any op-depth, including children of any underlying, replaced directories. */ -SELECT local_relpath FROM nodes +SELECT DISTINCT local_relpath FROM nodes WHERE wc_id = ?1 AND parent_relpath = ?2 +ORDER BY local_relpath -- STMT_SELECT_WORKING_CHILDREN /* Return all paths that are children of the working version of the directory (?1, ?2). A given path is not included just because it is a child of an underlying (replaced) directory, it has to be in the working version of the directory. */ -SELECT local_relpath FROM nodes +SELECT DISTINCT local_relpath FROM nodes WHERE wc_id = ?1 AND parent_relpath = ?2 AND (op_depth > (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2) OR (op_depth = (SELECT MAX(op_depth) FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2) - AND presence != MAP_BASE_DELETED)) + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE))) +ORDER BY local_relpath + +-- STMT_SELECT_BASE_NOT_PRESENT_CHILDREN +SELECT local_relpath FROM nodes +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 + AND presence = MAP_NOT_PRESENT +ORDER BY local_relpath -- STMT_SELECT_NODE_PROPS SELECT properties, presence FROM nodes @@ -396,25 +464,13 @@ SELECT dav_cache FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 -- STMT_SELECT_DELETION_INFO -SELECT (SELECT b.presence FROM nodes AS b - WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0), - work.presence, work.op_depth -FROM nodes_current AS work -WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0 -LIMIT 1 - --- STMT_SELECT_DELETION_INFO_SCAN -/* ### FIXME. moved.moved_to IS NOT NULL works when there is - only one move but we need something else when there are several. */ -SELECT (SELECT b.presence FROM nodes AS b - WHERE b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0), - work.presence, work.op_depth, moved.moved_to -FROM nodes_current AS work -LEFT OUTER JOIN nodes AS moved - ON moved.wc_id = work.wc_id - AND moved.local_relpath = work.local_relpath - AND moved.moved_to IS NOT NULL -WHERE work.wc_id = ?1 AND work.local_relpath = ?2 AND work.op_depth > 0 +SELECT b.presence, w.presence, w.op_depth, w.moved_to +FROM nodes w +LEFT JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = ?2 AND b.op_depth = 0 +WHERE w.wc_id = ?1 AND w.local_relpath = ?2 + AND w.op_depth = (SELECT MAX(op_depth) FROM nodes d + WHERE d.wc_id = ?1 AND d.local_relpath = ?2 + AND d.op_depth > 0) LIMIT 1 -- STMT_SELECT_MOVED_TO_NODE @@ -424,24 +480,20 @@ WHERE wc_id = ?1 AND local_relpath = ?2 AND moved_to IS NOT NULL ORDER BY op_depth DESC -- STMT_SELECT_OP_DEPTH_MOVED_TO -SELECT op_depth, moved_to, repos_path, revision +SELECT op_depth, moved_to FROM nodes -WHERE wc_id = ?1 AND local_relpath = ?2 - AND op_depth <= (SELECT MIN(op_depth) FROM nodes - WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3) -ORDER BY op_depth DESC +WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 + AND EXISTS(SELECT * from nodes + WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE)) +ORDER BY op_depth ASC +LIMIT 1 -- STMT_SELECT_MOVED_TO SELECT moved_to FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 --- STMT_SELECT_MOVED_HERE -SELECT moved_here, presence, repos_path, revision -FROM nodes -WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth >= ?3 -ORDER BY op_depth - -- STMT_SELECT_MOVED_BACK SELECT u.local_relpath, u.presence, u.repos_id, u.repos_path, u.revision, @@ -467,13 +519,6 @@ WHERE u.wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(u.local_relpath, ?2) AND u.op_depth = ?4 --- STMT_DELETE_MOVED_BACK -DELETE FROM nodes -WHERE wc_id = ?1 - AND (local_relpath = ?2 - OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) - AND op_depth = ?3 - -- STMT_DELETE_LOCK DELETE FROM lock WHERE repos_id = ?1 AND repos_relpath = ?2 @@ -498,7 +543,7 @@ UPDATE nodes SET repos_id = ?4, dav_cache = NULL WHERE (wc_id = ?1 AND local_relpath = ?2 AND repos_id = ?3) OR (wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND repos_id = ?3) - + -- STMT_UPDATE_LOCK_REPOS_ID UPDATE lock SET repos_id = ?2 @@ -677,6 +722,13 @@ INSERT OR IGNORE INTO actual_node ( SELECT wc_id, local_relpath, parent_relpath FROM targets_list +-- STMT_INSERT_ACTUAL_EMPTIES_FILES +INSERT OR IGNORE INTO actual_node ( + wc_id, local_relpath, parent_relpath) +SELECT wc_id, local_relpath, parent_relpath +FROM targets_list +WHERE kind=MAP_FILE + -- STMT_DELETE_ACTUAL_EMPTY DELETE FROM actual_node WHERE wc_id = ?1 AND local_relpath = ?2 @@ -691,7 +743,7 @@ WHERE wc_id = ?1 AND local_relpath = ?2 -- STMT_DELETE_ACTUAL_EMPTIES DELETE FROM actual_node WHERE wc_id = ?1 - AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) + AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) AND properties IS NULL AND conflict_data IS NULL AND changelist IS NULL @@ -755,7 +807,7 @@ WHERE wc_id = ?1 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) AND (changelist IS NULL OR NOT EXISTS (SELECT 1 FROM nodes_current c - WHERE c.wc_id = ?1 + WHERE c.wc_id = ?1 AND c.local_relpath = actual_node.local_relpath AND c.kind = MAP_FILE)) @@ -770,6 +822,17 @@ SET properties = NULL, right_checksum = NULL WHERE wc_id = ?1 AND local_relpath = ?2 +-- STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT +UPDATE actual_node +SET properties = NULL, + text_mod = NULL, + tree_conflict_data = NULL, + older_checksum = NULL, + left_checksum = NULL, + right_checksum = NULL, + changelist = NULL +WHERE wc_id = ?1 AND local_relpath = ?2 + -- STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE UPDATE actual_node SET properties = NULL, @@ -787,6 +850,7 @@ WHERE wc_id = ?1 UPDATE nodes SET depth = ?3 WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 AND kind=MAP_DIR + AND presence IN (MAP_NORMAL, MAP_INCOMPLETE) -- STMT_UPDATE_NODE_BASE_PRESENCE UPDATE nodes SET presence = ?3 @@ -891,6 +955,14 @@ SELECT local_dir_relpath FROM wc_lock WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_dir_relpath, ?2) +-- STMT_FIND_CONFLICT_DESCENDANT +SELECT 1 FROM actual_node +WHERE wc_id = ?1 + AND local_relpath > (?2 || '/') + AND local_relpath < (?2 || '0') /* '0' = ascii('/') +1 */ + AND conflict_data IS NOT NULL +LIMIT 1 + -- STMT_DELETE_WC_LOCK_ORPHAN DELETE FROM wc_lock WHERE wc_id = ?1 AND local_dir_relpath = ?2 @@ -924,22 +996,11 @@ VALUES (?1, ?2, 0, AND op_depth = 0)) -- STMT_INSTALL_WORKING_NODE_FOR_DELETE -INSERT OR REPLACE INTO nodes ( +INSERT INTO nodes ( wc_id, local_relpath, op_depth, parent_relpath, presence, kind) VALUES(?1, ?2, ?3, ?4, MAP_BASE_DELETED, ?5) --- STMT_DELETE_NO_LOWER_LAYER -DELETE FROM nodes - WHERE wc_id = ?1 - AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) - AND op_depth = ?3 - AND NOT EXISTS (SELECT 1 FROM nodes n - WHERE n.wc_id = ?1 - AND n.local_relpath = nodes.local_relpath - AND n.op_depth = ?4 - AND n.presence IN (MAP_NORMAL, MAP_INCOMPLETE)) - -- STMT_REPLACE_WITH_BASE_DELETED INSERT OR REPLACE INTO nodes (wc_id, local_relpath, op_depth, parent_relpath, kind, moved_to, presence) @@ -947,33 +1008,41 @@ SELECT wc_id, local_relpath, op_depth, parent_relpath, kind, moved_to, MAP_BASE_DELETED FROM nodes WHERE wc_id = ?1 - AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) + AND local_relpath = ?2 AND op_depth = ?3 -/* If this query is updated, STMT_INSERT_DELETE_LIST should too. */ +/* If this query is updated, STMT_INSERT_DELETE_LIST should too. + Use UNION ALL instead of a simple 'OR' to avoid creating a temp table */ -- STMT_INSERT_DELETE_FROM_NODE_RECURSIVE INSERT INTO nodes ( wc_id, local_relpath, op_depth, parent_relpath, presence, kind) SELECT wc_id, local_relpath, ?4 /*op_depth*/, parent_relpath, MAP_BASE_DELETED, kind FROM nodes +WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3 +UNION ALL +SELECT wc_id, local_relpath, ?4 /*op_depth*/, parent_relpath, MAP_BASE_DELETED, + kind +FROM nodes WHERE wc_id = ?1 - AND (local_relpath = ?2 - OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) + AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND op_depth = ?3 AND presence NOT IN (MAP_BASE_DELETED, MAP_NOT_PRESENT, MAP_EXCLUDED, MAP_SERVER_EXCLUDED) AND file_external IS NULL +ORDER BY local_relpath -- STMT_INSERT_WORKING_NODE_FROM_BASE_COPY -INSERT INTO nodes ( +INSERT OR REPLACE INTO nodes ( wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, revision, presence, depth, kind, changed_revision, changed_date, changed_author, checksum, properties, translated_size, last_mod_time, - symlink_target ) + symlink_target, moved_to ) SELECT wc_id, local_relpath, ?3 /*op_depth*/, parent_relpath, repos_id, repos_path, revision, presence, depth, kind, changed_revision, changed_date, changed_author, checksum, properties, translated_size, - last_mod_time, symlink_target + last_mod_time, symlink_target, + (SELECT moved_to FROM nodes + WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3) moved_to FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0 @@ -992,11 +1061,30 @@ WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND op_depth = ?3 --- STMT_UPDATE_OP_DEPTH_RECURSIVE -UPDATE nodes SET op_depth = ?4, moved_here = NULL -WHERE wc_id = ?1 - AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) - AND op_depth = ?3 +/* Duplicated SELECT body to avoid creating temporary table */ +-- STMT_COPY_OP_DEPTH_RECURSIVE +INSERT INTO nodes ( + wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path, + revision, presence, depth, kind, changed_revision, changed_date, + changed_author, checksum, properties, translated_size, last_mod_time, + symlink_target, moved_here, moved_to ) +SELECT + wc_id, local_relpath, ?4, parent_relpath, repos_id, + repos_path, revision, presence, depth, kind, changed_revision, + changed_date, changed_author, checksum, properties, translated_size, + last_mod_time, symlink_target, NULL, NULL +FROM nodes +WHERE wc_id = ?1 AND op_depth = ?3 AND local_relpath = ?2 +UNION ALL +SELECT + wc_id, local_relpath, ?4, parent_relpath, repos_id, + repos_path, revision, presence, depth, kind, changed_revision, + changed_date, changed_author, checksum, properties, translated_size, + last_mod_time, symlink_target, NULL, NULL +FROM nodes +WHERE wc_id = ?1 AND op_depth = ?3 + AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) +ORDER BY local_relpath -- STMT_DOES_NODE_EXIST SELECT 1 FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2 @@ -1198,7 +1286,10 @@ WHERE (wc_id = ?1 AND local_relpath = ?2) OR (wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) -- STMT_PRAGMA_LOCKING_MODE -PRAGMA locking_mode = exclusive +PRAGMA locking_mode = exclusive; +/* Testing shows DELETE is faster than TRUNCATE on NFS and + exclusive-locking is mostly used on remote file systems. */ +PRAGMA journal_mode = DELETE /* ------------------------------------------------------------------------- */ @@ -1213,14 +1304,6 @@ VALUES (?1, ?2, ?3, ?4, ?5, ?6) /* these are used in upgrade.c */ --- STMT_UPDATE_ACTUAL_CONFLICT_DATA -UPDATE actual_node SET conflict_data = ?3 -WHERE wc_id = ?1 AND local_relpath = ?2 - --- STMT_INSERT_ACTUAL_CONFLICT_DATA -INSERT INTO actual_node (wc_id, local_relpath, conflict_data, parent_relpath) -VALUES (?1, ?2, ?3, ?4) - -- STMT_SELECT_ALL_FILES SELECT local_relpath FROM nodes_current WHERE wc_id = ?1 AND parent_relpath = ?2 AND kind = MAP_FILE @@ -1333,8 +1416,9 @@ BEGIN WHERE n.wc_id = OLD.wc_id AND n.local_relpath = OLD.local_relpath) THEN 1 - ELSE NULL - END; + END notify + WHERE OLD.conflict_data IS NOT NULL + OR notify IS NOT NULL; END; DROP TRIGGER IF EXISTS trigger_revert_list_actual_update; CREATE TEMPORARY TRIGGER trigger_revert_list_actual_update @@ -1350,8 +1434,9 @@ BEGIN WHERE n.wc_id = OLD.wc_id AND n.local_relpath = OLD.local_relpath) THEN 1 - ELSE NULL - END; + END notify + WHERE OLD.conflict_data IS NOT NULL + OR notify IS NOT NULL; END -- STMT_DROP_REVERT_LIST_TRIGGERS @@ -1377,12 +1462,15 @@ ORDER BY local_relpath DELETE FROM revert_list WHERE local_relpath = ?1 -- STMT_SELECT_REVERT_LIST_RECURSIVE -SELECT DISTINCT local_relpath -FROM revert_list -WHERE (local_relpath = ?1 - OR IS_STRICT_DESCENDANT_OF(local_relpath, ?1)) - AND (notify OR actual = 0) -ORDER BY local_relpath +SELECT p.local_relpath, n.kind, a.notify, a.kind +FROM (SELECT DISTINCT local_relpath + FROM revert_list + WHERE (local_relpath = ?1 + OR IS_STRICT_DESCENDANT_OF(local_relpath, ?1))) p + +LEFT JOIN revert_list n ON n.local_relpath=p.local_relpath AND n.actual=0 +LEFT JOIN revert_list a ON a.local_relpath=p.local_relpath AND a.actual=1 +ORDER BY p.local_relpath -- STMT_DELETE_REVERT_LIST_RECURSIVE DELETE FROM revert_list @@ -1404,16 +1492,18 @@ CREATE TEMPORARY TABLE delete_list ( A subquery is used instead of nodes_current to avoid a table scan */ -- STMT_INSERT_DELETE_LIST INSERT INTO delete_list(local_relpath) +SELECT ?2 +UNION ALL SELECT local_relpath FROM nodes AS n WHERE wc_id = ?1 - AND (local_relpath = ?2 - OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) + AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) AND op_depth >= ?3 AND op_depth = (SELECT MAX(s.op_depth) FROM nodes AS s WHERE s.wc_id = ?1 AND s.local_relpath = n.local_relpath) AND presence NOT IN (MAP_BASE_DELETED, MAP_NOT_PRESENT, MAP_EXCLUDED, MAP_SERVER_EXCLUDED) AND file_external IS NULL +ORDER by local_relpath -- STMT_SELECT_DELETE_LIST SELECT local_relpath FROM delete_list @@ -1429,7 +1519,7 @@ CREATE TEMPORARY TABLE update_move_list ( ### working copies. queries, etc will need to be adjusted. */ local_relpath TEXT PRIMARY KEY NOT NULL UNIQUE, action INTEGER NOT NULL, - kind INTEGER NOT NULL, + kind TEXT NOT NULL, content_state INTEGER NOT NULL, prop_state INTEGER NOT NULL ) @@ -1447,6 +1537,11 @@ ORDER BY local_relpath -- STMT_FINALIZE_UPDATE_MOVE DROP TABLE IF EXISTS update_move_list +-- STMT_MOVE_NOTIFY_TO_REVERT +INSERT INTO revert_list (local_relpath, notify, kind, actual) + SELECT local_relpath, 2, kind, 1 FROM update_move_list; +DROP TABLE update_move_list + /* ------------------------------------------------------------------------- */ /* Queries for revision status. */ @@ -1499,16 +1594,6 @@ WHERE wc_id = ?1 AND repos_path IS NOT RELPATH_SKIP_JOIN(?2, ?3, local_relpath) LIMIT 1 --- STMT_SELECT_BASE_FILES_RECURSIVE -SELECT local_relpath, translated_size, last_mod_time FROM nodes AS n -WHERE wc_id = ?1 - AND (local_relpath = ?2 - OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) - AND op_depth = 0 - AND kind=MAP_FILE - AND presence=MAP_NORMAL - AND file_external IS NULL - -- STMT_SELECT_MOVED_FROM_RELPATH SELECT local_relpath, op_depth FROM nodes WHERE wc_id = ?1 AND moved_to = ?2 AND op_depth > 0 @@ -1571,31 +1656,30 @@ UPDATE nodes SET moved_to = NULL WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(moved_to, ?2) - -/* This statement returns pairs of move-roots below the path ?2 in WC_ID ?1, - * where the source of the move is within the subtree rooted at path ?2, and - * the destination of the move is outside the subtree rooted at path ?2. */ --- STMT_SELECT_MOVED_PAIR2 -SELECT local_relpath, moved_to, op_depth FROM nodes -WHERE wc_id = ?1 - AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2)) - AND moved_to IS NOT NULL - AND NOT IS_STRICT_DESCENDANT_OF(moved_to, ?2) - AND op_depth >= (SELECT MAX(op_depth) FROM nodes o - WHERE o.wc_id = ?1 - AND o.local_relpath = ?2) - -- STMT_SELECT_MOVED_PAIR3 -SELECT local_relpath, moved_to, op_depth, kind FROM nodes -WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3 - AND moved_to IS NOT NULL +SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind +FROM nodes n +JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath + AND d.op_depth = (SELECT MIN(dd.op_depth) + FROM nodes dd + WHERE dd.wc_id = ?1 + AND dd.local_relpath = d.local_relpath + AND dd.op_depth > ?3) +WHERE n.wc_id = ?1 AND n.local_relpath = ?2 AND n.op_depth = ?3 + AND d.moved_to IS NOT NULL UNION ALL -SELECT local_relpath, moved_to, op_depth, kind FROM nodes -WHERE wc_id = ?1 - AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2) - AND op_depth > ?3 - AND moved_to IS NOT NULL -ORDER BY local_relpath, op_depth +SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind +FROM nodes n +JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath + AND d.op_depth = (SELECT MIN(dd.op_depth) + FROM nodes dd + WHERE dd.wc_id = ?1 + AND dd.local_relpath = d.local_relpath + AND dd.op_depth > ?3) +WHERE n.wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2) + AND n.op_depth = ?3 + AND d.moved_to IS NOT NULL +ORDER BY n.local_relpath -- STMT_SELECT_MOVED_OUTSIDE SELECT local_relpath, moved_to, op_depth FROM nodes @@ -1605,40 +1689,22 @@ WHERE wc_id = ?1 AND moved_to IS NOT NULL AND NOT IS_STRICT_DESCENDANT_OF(moved_to, ?2) --- STMT_SELECT_OP_DEPTH_MOVED_PAIR -SELECT n.local_relpath, n.moved_to, - (SELECT o.repos_path FROM nodes AS o - WHERE o.wc_id = n.wc_id - AND o.local_relpath = n.local_relpath - AND o.op_depth < ?3 ORDER BY o.op_depth DESC LIMIT 1) -FROM nodes AS n -WHERE n.wc_id = ?1 - AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2) - AND n.op_depth = ?3 - AND n.moved_to IS NOT NULL - --- STMT_SELECT_MOVED_DESCENDANTS -SELECT n.local_relpath, h.moved_to -FROM nodes n, nodes h -WHERE n.wc_id = ?1 - AND h.wc_id = ?1 - AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2) - AND h.local_relpath = n.local_relpath - AND n.op_depth = ?3 - AND h.op_depth = (SELECT MIN(o.op_depth) - FROM nodes o - WHERE o.wc_id = ?1 - AND o.local_relpath = n.local_relpath - AND o.op_depth > ?3) - AND h.moved_to IS NOT NULL +-- STMT_SELECT_MOVED_DESCENDANTS_SRC +SELECT s.op_depth, n.local_relpath, n.kind, n.repos_path, s.moved_to +FROM nodes n +JOIN nodes s ON s.wc_id = n.wc_id AND s.local_relpath = n.local_relpath + AND s.op_depth = (SELECT MIN(d.op_depth) + FROM nodes d + WHERE d.wc_id = ?1 + AND d.local_relpath = s.local_relpath + AND d.op_depth > ?3) +WHERE n.wc_id = ?1 AND n.op_depth = ?3 + AND (n.local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)) + AND s.moved_to IS NOT NULL -- STMT_COMMIT_UPDATE_ORIGIN -/* Note that the only reason this SUBSTR() trick is valid is that you - can move neither the working copy nor the repository root. - - SUBSTR(local_relpath, LENGTH(?2)+1) includes the '/' of the path */ UPDATE nodes SET repos_id = ?4, - repos_path = ?5 || SUBSTR(local_relpath, LENGTH(?2)+1), + repos_path = RELPATH_SKIP_JOIN(?2, ?5, local_relpath), revision = ?6 WHERE wc_id = ?1 AND (local_relpath = ?2 @@ -1658,14 +1724,16 @@ ORDER BY local_relpath -- STMT_SELECT_HAS_NON_FILE_CHILDREN SELECT 1 FROM nodes -WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = 0 AND kind != MAP_FILE +WHERE wc_id = ?1 AND parent_relpath = ?2 AND op_depth = ?3 AND kind != MAP_FILE +LIMIT 1 -- STMT_SELECT_HAS_GRANDCHILDREN SELECT 1 FROM nodes WHERE wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(parent_relpath, ?2) - AND op_depth = 0 + AND op_depth = ?3 AND file_external IS NULL +LIMIT 1 /* ------------------------------------------------------------------------- */ diff --git a/subversion/libsvn_wc/wc.h b/subversion/libsvn_wc/wc.h index d7cf017..ab6870d 100644 --- a/subversion/libsvn_wc/wc.h +++ b/subversion/libsvn_wc/wc.h @@ -158,7 +158,7 @@ extern "C" { * Bumped in r1395109. * * == 1.8.x shipped with format 31 - * + * * Please document any further format changes here. */ @@ -253,52 +253,6 @@ svn_wc__context_create_with_db(svn_wc_context_t **wc_ctx, apr_pool_t * svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue); - -/** Internal helper for svn_wc_process_committed_queue2(). - * - * ### If @a queue is NULL, then ...? - * ### else: - * Bump an item from @a queue (the one associated with @a - * local_abspath) to @a new_revnum after a commit succeeds, recursing - * if @a recurse is set. - * - * @a new_date is the (server-side) date of the new revision, or 0. - * - * @a rev_author is the (server-side) author of the new - * revision; it may be @c NULL. - * - * @a new_dav_cache is a hash of dav property changes to be made to - * the @a local_abspath. - * ### [JAF] Is it? See svn_wc_queue_committed3(). It ends up being - * ### assigned as a whole to wc.db:BASE_NODE:dav_cache. - * - * If @a no_unlock is set, don't release any user locks on @a - * local_abspath; otherwise release them as part of this processing. - * - * If @a keep_changelist is set, don't remove any changeset assignments - * from @a local_abspath; otherwise, clear it of such assignments. - * - * If @a sha1_checksum is non-NULL, use it to identify the node's pristine - * text. - * - * Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly - * recursive commit operation. - */ -svn_error_t * -svn_wc__process_committed_internal(svn_wc__db_t *db, - const char *local_abspath, - svn_boolean_t recurse, - svn_boolean_t top_of_recurse, - svn_revnum_t new_revnum, - apr_time_t new_date, - const char *rev_author, - apr_hash_t *new_dav_cache, - svn_boolean_t no_unlock, - svn_boolean_t keep_changelist, - const svn_checksum_t *sha1_checksum, - const svn_wc_committed_queue_t *queue, - apr_pool_t *scratch_pool); - /*** Update traversals. ***/ @@ -612,14 +566,6 @@ svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db, void *cancel_baton, apr_pool_t *scratch_pool); -/* Library-internal version of svn_wc__node_get_schedule(). */ -svn_error_t * -svn_wc__internal_node_get_schedule(svn_wc_schedule_t *schedule, - svn_boolean_t *copied, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool); - /* Internal version of svn_wc__node_get_origin() */ svn_error_t * svn_wc__internal_get_origin(svn_boolean_t *is_copy, @@ -627,6 +573,7 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, + svn_depth_t *depth, const char **copy_root_abspath, svn_wc__db_t *db, const char *local_abspath, @@ -634,17 +581,6 @@ svn_wc__internal_get_origin(svn_boolean_t *is_copy, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* Internal version of svn_wc__node_get_repos_info() */ -svn_error_t * -svn_wc__internal_get_repos_info(svn_revnum_t *revision, - const char **repos_relpath, - const char **repos_root_url, - const char **repos_uuid, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - /* Upgrade the wc sqlite database given in SDB for the wc located at WCROOT_ABSPATH. It's current/starting format is given by START_FORMAT. After the upgrade is complete (to as far as the automatic upgrade will @@ -699,9 +635,11 @@ svn_wc__write_check(svn_wc__db_t *db, */ svn_error_t * svn_wc__read_conflicts(const apr_array_header_t **conflicts, + svn_skel_t **conflict_skel, svn_wc__db_t *db, const char *local_abspath, svn_boolean_t create_tempfiles, + svn_boolean_t only_tree_conflict, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -785,24 +723,12 @@ svn_wc__externals_find_target_dups(apr_array_header_t **duplicate_targets, apr_pool_t *pool, apr_pool_t *scratch_pool); -/* Revert tree LOCAL_ABSPATH to depth DEPTH and notify for all - reverts. */ -svn_error_t * -svn_wc__revert_internal(svn_wc__db_t *db, - const char *local_abspath, - svn_depth_t depth, - svn_boolean_t use_commit_times, - svn_cancel_func_t cancel_func, - void *cancel_baton, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool); - svn_error_t * svn_wc__node_has_local_mods(svn_boolean_t *modified, svn_boolean_t *all_edits_are_deletes, svn_wc__db_t *db, const char *local_abspath, + svn_boolean_t ignore_unversioned, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool); diff --git a/subversion/libsvn_wc/wc_db.c b/subversion/libsvn_wc/wc_db.c index 6c0dd61..ee17b85 100644 --- a/subversion/libsvn_wc/wc_db.c +++ b/subversion/libsvn_wc/wc_db.c @@ -27,6 +27,7 @@ #include <apr_pools.h> #include <apr_hash.h> +#include "svn_private_config.h" #include "svn_types.h" #include "svn_error.h" #include "svn_dirent_uri.h" @@ -48,7 +49,7 @@ #include "workqueue.h" #include "token-map.h" -#include "svn_private_config.h" +#include "private/svn_sorts_private.h" #include "private/svn_sqlite.h" #include "private/svn_skel.h" #include "private/svn_wc_private.h" @@ -277,10 +278,9 @@ add_work_items(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool); static svn_error_t * -set_actual_props(apr_int64_t wc_id, +set_actual_props(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_hash_t *props, - svn_sqlite__db_t *db, apr_pool_t *scratch_pool); static svn_error_t * @@ -354,13 +354,6 @@ convert_to_working_status(svn_wc__db_status_t *working_status, svn_wc__db_status_t status); static svn_error_t * -wclock_owns_lock(svn_boolean_t *own_lock, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_boolean_t exact, - apr_pool_t *scratch_pool); - -static svn_error_t * db_is_switched(svn_boolean_t *is_switched, svn_node_kind_t *kind, svn_wc__db_wcroot_t *wcroot, @@ -424,7 +417,7 @@ lock_from_columns(svn_sqlite__stmt_t *stmt, svn_error_t * svn_wc__db_fetch_repos_info(const char **repos_root_url, const char **repos_uuid, - svn_sqlite__db_t *sdb, + svn_wc__db_wcroot_t *wcroot, apr_int64_t repos_id, apr_pool_t *result_pool) { @@ -443,7 +436,7 @@ svn_wc__db_fetch_repos_info(const char **repos_root_url, return SVN_NO_ERROR; } - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_REPOSITORY_BY_ID)); SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -493,35 +486,6 @@ repos_location_from_columns(apr_int64_t *repos_id, } } - -/* Get the statement given by STMT_IDX, and bind the appropriate wc_id and - local_relpath based upon LOCAL_ABSPATH. Store it in *STMT, and use - SCRATCH_POOL for temporary allocations. - - Note: WC_ID and LOCAL_RELPATH must be arguments 1 and 2 in the statement. */ -static svn_error_t * -get_statement_for_path(svn_sqlite__stmt_t **stmt, - svn_wc__db_t *db, - const char *local_abspath, - int stmt_idx, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, - local_abspath, scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - - SVN_ERR(svn_sqlite__get_statement(stmt, wcroot->sdb, stmt_idx)); - SVN_ERR(svn_sqlite__bindf(*stmt, "is", wcroot->wc_id, local_relpath)); - - return SVN_NO_ERROR; -} - - /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID value. If one does not exist, then create a new one. */ static svn_error_t * @@ -575,12 +539,53 @@ blank_ibb(insert_base_baton_t *pibb) } -svn_error_t * -svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_node_kind_t kind, - int op_depth, - apr_pool_t *scratch_pool) +/* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH. + + ### What about KIND and OP_DEPTH? KIND ought to be redundant; I'm + discussing on dev@ whether we can let that be null for presence + == base-deleted. OP_DEPTH is the op-depth of what, and why? + It is used to select the lowest working node higher than OP_DEPTH, + so, in terms of the API, OP_DEPTH means ...? + + Given a wc: + + 0 1 2 3 4 + normal + A normal + A/B normal normal + A/B/C not-pres normal + A/B/C/D normal + + That is checkout, delete A/B, copy a replacement A/B, delete copied + child A/B/C, add replacement A/B/C, add A/B/C/D. + + Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E + must extend the A/B deletion: + + 0 1 2 3 4 + normal + A normal + A/B normal normal + A/B/C normal not-pres normal + A/B/C/D normal base-del normal + A/B/C/D/E normal base-del + + When adding a node if the parent has a higher working node then the + parent node is deleted (or replaced) and the delete must be extended + to cover new node. + + In the example above A/B/C/D and A/B/C/D/E are the nodes that get + the extended delete, A/B/C is already deleted. + + If ADDED_DELETE is not NULL, set *ADDED_DELETE to TRUE if a new delete + was recorded, otherwise to FALSE. + */ +static svn_error_t * +db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_node_kind_t kind, + int op_depth, + apr_pool_t *scratch_pool) { svn_boolean_t have_row; svn_sqlite__stmt_t *stmt; @@ -622,7 +627,7 @@ svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, } -/* This is the reverse of svn_wc__db_extend_parent_delete. +/* This is the reverse of db_extend_parent_delete. When removing a node if the parent has a higher working node then the parent node and this node are both deleted or replaced and any @@ -632,11 +637,11 @@ svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, only uses this function within an sqlite transaction if atomic behavior is needed. */ -svn_error_t * -svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - int op_depth, - apr_pool_t *scratch_pool) +static svn_error_t * +db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; @@ -825,8 +830,8 @@ insert_base_node(const insert_base_baton_t *pibb, new_actual_props = NULL; } - SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props, - wcroot->sdb, scratch_pool)); + SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props, + scratch_pool)); } if (pibb->kind == svn_node_dir && pibb->children) @@ -847,16 +852,16 @@ insert_base_node(const insert_base_baton_t *pibb, || (pibb->status == svn_wc__db_status_incomplete)) && ! pibb->file_external) { - SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath, - pibb->kind, 0, - scratch_pool)); + SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, + pibb->kind, 0, + scratch_pool)); } else if (pibb->status == svn_wc__db_status_not_present || pibb->status == svn_wc__db_status_server_excluded || pibb->status == svn_wc__db_status_excluded) { - SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0, - scratch_pool)); + SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, + scratch_pool)); } } @@ -1101,8 +1106,8 @@ insert_working_node(const insert_working_baton_t *piwb, new_actual_props = NULL; } - SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props, - wcroot->sdb, scratch_pool)); + SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props, + scratch_pool)); } if (piwb->kind == svn_node_dir) @@ -1148,159 +1153,44 @@ insert_working_node(const insert_working_baton_t *piwb, } -/* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key - pointed to the same name. */ -static svn_error_t * -add_children_to_hash(apr_hash_t *children, - int stmt_idx, - svn_sqlite__db_t *sdb, - apr_int64_t wc_id, - const char *parent_relpath, - apr_pool_t *result_pool) -{ - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - while (have_row) - { - const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *name = svn_relpath_basename(child_relpath, result_pool); - - svn_hash_sets(children, name, name); - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - - return svn_sqlite__reset(stmt); -} - - -/* Set *CHILDREN to a new array of the (const char *) basenames of the - immediate children, whatever their status, of the working node at - LOCAL_RELPATH. */ -static svn_error_t * -gather_children2(const apr_array_header_t **children, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - apr_hash_t *names_hash = apr_hash_make(scratch_pool); - apr_array_header_t *names_array; - - /* All of the names get allocated in RESULT_POOL. It - appears to be faster to use the hash to remove duplicates than to - use DISTINCT in the SQL query. */ - SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN, - wcroot->sdb, wcroot->wc_id, - local_relpath, result_pool)); - - SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool)); - *children = names_array; - return SVN_NO_ERROR; -} - /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH, of any status, in all op-depths in the NODES table. */ static svn_error_t * gather_children(const apr_array_header_t **children, svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, + const char *parent_relpath, + int stmt_idx, + int op_depth, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_hash_t *names_hash = apr_hash_make(scratch_pool); - apr_array_header_t *names_array; - - /* All of the names get allocated in RESULT_POOL. It - appears to be faster to use the hash to remove duplicates than to - use DISTINCT in the SQL query. */ - SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN, - wcroot->sdb, wcroot->wc_id, - local_relpath, result_pool)); - - SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool)); - *children = names_array; - return SVN_NO_ERROR; -} - - -/* Set *CHILDREN to a new array of (const char *) names of the children of - the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH - - that is, only the children that are at the same op-depth as their parent. */ -static svn_error_t * -gather_repo_children(const apr_array_header_t **children, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - int op_depth, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - apr_array_header_t *result - = apr_array_make(result_pool, 0, sizeof(const char *)); + apr_array_header_t *result; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_OP_DEPTH_CHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - while (have_row) - { - const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - - /* Allocate the name in RESULT_POOL so we won't have to copy it. */ - APR_ARRAY_PUSH(result, const char *) - = svn_relpath_basename(child_relpath, result_pool); + result = apr_array_make(result_pool, 16, sizeof(const char*)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - SVN_ERR(svn_sqlite__reset(stmt)); - - *children = result; - return SVN_NO_ERROR; -} - -svn_error_t * -svn_wc__db_get_children_op_depth(apr_hash_t **children, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - int op_depth, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - - *children = apr_hash_make(result_pool); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath)); + if (op_depth >= 0) + SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth)); - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_OP_DEPTH_CHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - svn_node_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_node_kind_t)); + const char *name = svn_relpath_basename(child_relpath, result_pool); - *child_kind = svn_sqlite__column_token(stmt, 1, kind_map); - svn_hash_sets(*children, - svn_relpath_basename(child_relpath, result_pool), - child_kind); + APR_ARRAY_PUSH(result, const char *) = name; SVN_ERR(svn_sqlite__step(&have_row, stmt)); } - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + *children = result; return SVN_NO_ERROR; } - /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH. * Else, return FALSE. */ static svn_boolean_t @@ -1358,7 +1248,7 @@ flush_entries(svn_wc__db_wcroot_t *wcroot, hi; hi = apr_hash_next(hi)) { - const char *item_abspath = svn__apr_hash_index_key(hi); + const char *item_abspath = apr_hash_this_key(hi); if ((depth == svn_depth_files || depth == svn_depth_immediates) && is_immediate_child_path(local_abspath, item_abspath)) @@ -1485,12 +1375,12 @@ init_db(/* output values */ SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS)); SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS)); + SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool)); + /* Insert the repository. */ SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid, db, scratch_pool)); - SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool)); - /* Insert the wcroot. */ /* ### Right now, this just assumes wc metadata is being stored locally. */ SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT)); @@ -1508,7 +1398,7 @@ init_db(/* output values */ *wc_id, /* 1 */ "", /* 2 */ 0, /* op_depth is 0 for base */ - NULL, /* 4 */ + SVN_VA_NULL, /* 4 */ *repos_id, root_node_repos_relpath, root_node_revision, @@ -1544,11 +1434,13 @@ create_db(svn_sqlite__db_t **sdb, svn_revnum_t root_node_revision, svn_depth_t root_node_depth, svn_boolean_t exclusive, + apr_int32_t timeout, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname, svn_sqlite__mode_rwcreate, exclusive, + timeout, NULL /* my_statements */, result_pool, scratch_pool)); @@ -1577,6 +1469,8 @@ svn_wc__db_init(svn_wc__db_t *db, apr_int64_t wc_id; svn_wc__db_wcroot_t *wcroot; svn_boolean_t sqlite_exclusive = FALSE; + apr_int32_t sqlite_timeout = 0; /* default timeout */ + apr_hash_index_t *hi; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); SVN_ERR_ASSERT(repos_relpath != NULL); @@ -1587,7 +1481,7 @@ svn_wc__db_init(svn_wc__db_t *db, /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */ - SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive, + SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive, SVN_CONFIG_SECTION_WORKING_COPY, SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE, FALSE)); @@ -1596,6 +1490,7 @@ svn_wc__db_init(svn_wc__db_t *db, SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url, repos_uuid, SDB_FILE, repos_relpath, initial_rev, depth, sqlite_exclusive, + sqlite_timeout, db->state_pool, scratch_pool)); /* Create the WCROOT for this directory. */ @@ -1603,9 +1498,31 @@ svn_wc__db_init(svn_wc__db_t *db, apr_pstrdup(db->state_pool, local_abspath), sdb, wc_id, FORMAT_FROM_SDB, FALSE /* auto-upgrade */, - FALSE /* enforce_empty_wq */, db->state_pool, scratch_pool)); + /* Any previously cached children may now have a new WCROOT, most likely that + of the new WCROOT, but there might be descendant directories that are their + own working copy, in which case setting WCROOT to our new WCROOT might + actually break things for those. + + Clearing is the safest thing we can do in this case, as a test would lead + to unnecessary probing, while the standard code probes later anyway. So we + only lose a bit of memory + + ### Perhaps we could check wcroot->abspath to detect which case we have + where, but currently it is already very hard to trigger this from + the short living 'svn' client. (GUI clients like TortoiseSVN are far + more likely to get in these cases) + */ + for (hi = apr_hash_first(scratch_pool, db->dir_data); + hi; + hi = apr_hash_next(hi)) + { + const char *abspath = apr_hash_this_key(hi); + if (svn_dirent_is_ancestor(wcroot->abspath, abspath)) + svn_hash_sets(db->dir_data, abspath, NULL); + } + /* The WCROOT is complete. Stash it into DB. */ svn_hash_sets(db->dir_data, wcroot->abspath, wcroot); @@ -1714,10 +1631,10 @@ svn_wc__db_base_add_directory(svn_wc__db_t *db, const apr_array_header_t *children, svn_depth_t depth, apr_hash_t *dav_cache, - const svn_skel_t *conflict, svn_boolean_t update_actual_props, apr_hash_t *new_actual_props, apr_array_header_t *new_iprops, + const svn_skel_t *conflict, const svn_skel_t *work_items, apr_pool_t *scratch_pool) { @@ -2126,32 +2043,63 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t *db, } /* Recursively clear moved-here information at the copy-half of the move - * which moved the node at SRC_RELPATH away. This transforms the move into - * a simple copy. */ + * which moved a node to MOVED_TO_RELPATH. This transforms this side of the + * move into a simple copy. + */ static svn_error_t * -clear_moved_here(const char *src_relpath, - svn_wc__db_wcroot_t *wcroot, +clear_moved_here(svn_wc__db_wcroot_t *wcroot, + const char *moved_to_relpath, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; - const char *dst_relpath; - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - src_relpath, relpath_depth(src_relpath))); - SVN_ERR(svn_sqlite__step_row(stmt)); - dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); - SVN_ERR(svn_sqlite__reset(stmt)); + int affected_rows; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_CLEAR_MOVED_HERE_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - dst_relpath, relpath_depth(dst_relpath))); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, moved_to_relpath, + relpath_depth(moved_to_relpath))); + + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + + if (affected_rows == 0) + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The node '%s' was not found."), + path_for_error_message(wcroot, moved_to_relpath, + scratch_pool)); return SVN_NO_ERROR; } +svn_error_t * +svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_relpath, + int delete_op_depth, + const char *dst_relpath, + const svn_skel_t *work_items, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + int affected; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_CLEAR_MOVED_TO_RELPATH)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath, + delete_op_depth)); + SVN_ERR(svn_sqlite__update(&affected, stmt)); + + if (affected != 1) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("Path '%s' is not moved"), + path_for_error_message(wcroot, src_relpath, + scratch_pool)); + + SVN_ERR(clear_moved_here(wcroot, dst_relpath, scratch_pool)); + + SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); + return SVN_NO_ERROR; +} + + /* The body of svn_wc__db_base_remove(). */ static svn_error_t * @@ -2159,9 +2107,9 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, svn_wc__db_t *db, /* For checking conflicts */ svn_boolean_t keep_as_working, - svn_boolean_t queue_deletes, - svn_boolean_t remove_locks, - svn_revnum_t not_present_revision, + svn_boolean_t mark_not_present, + svn_boolean_t mark_excluded, + svn_revnum_t marker_revision, svn_skel_t *conflict, svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -2169,66 +2117,82 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; svn_wc__db_status_t status; + svn_revnum_t revision; apr_int64_t repos_id; const char *repos_relpath; svn_node_kind_t kind; svn_boolean_t keep_working; + int op_depth; + svn_node_kind_t wrk_kind; + svn_boolean_t no_delete_wc = FALSE; + svn_boolean_t file_external; - SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL, + SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision, &repos_relpath, &repos_id, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + &file_external, wcroot, local_relpath, scratch_pool, scratch_pool)); - if (remove_locks) - { - svn_sqlite__stmt_t *lock_stmt; + /* Check if there is already a working node */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_NODE_INFO)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); - SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb, - STMT_DELETE_LOCK_RECURSIVELY)); - SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath)); - SVN_ERR(svn_sqlite__step_done(lock_stmt)); - } + if (!have_row) + return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */ + + op_depth = svn_sqlite__column_int(stmt, 0); + wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map); - if (status == svn_wc__db_status_normal - && keep_as_working) + if (op_depth > 0 + && op_depth == relpath_depth(local_relpath)) { - SVN_ERR(svn_wc__db_op_make_copy(db, - svn_dirent_join(wcroot->abspath, - local_relpath, - scratch_pool), - NULL, NULL, - scratch_pool)); - keep_working = TRUE; + svn_wc__db_status_t presence; + presence = svn_sqlite__column_token(stmt, 3, presence_map); + + if (presence == svn_wc__db_status_base_deleted) + { + keep_working = FALSE; + no_delete_wc = TRUE; + } + else + { + keep_working = TRUE; + } } else + keep_working = FALSE; + SVN_ERR(svn_sqlite__reset(stmt)); + + if (keep_as_working && op_depth == 0) { - /* Check if there is already a working node */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step(&keep_working, stmt)); - SVN_ERR(svn_sqlite__reset(stmt)); + if (status == svn_wc__db_status_normal + || status == svn_wc__db_status_incomplete) + { + SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE, + NULL, NULL, + scratch_pool)); + } + keep_working = TRUE; } /* Step 1: Create workqueue operations to remove files and dirs in the local-wc */ - if (!keep_working - && queue_deletes - && (status == svn_wc__db_status_normal - || status == svn_wc__db_status_incomplete)) + if (!keep_working && !no_delete_wc) { svn_skel_t *work_item; const char *local_abspath; local_abspath = svn_dirent_join(wcroot->abspath, local_relpath, scratch_pool); - if (kind == svn_node_dir) + if (wrk_kind == svn_node_dir) { apr_pool_t *iterpool; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_BASE_PRESENT)); + STMT_SELECT_WORKING_PRESENT)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); iterpool = svn_pool_create(scratch_pool); @@ -2307,27 +2271,12 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, ACTUAL_NODE records */ /* Step 3: Delete WORKING nodes */ - if (conflict) + if (!keep_working) { apr_pool_t *iterpool; - /* - * When deleting a conflicted node, moves of any moved-outside children - * of the node must be broken. Else, the destination will still be marked - * moved-here after the move source disappears from the working copy. - * - * ### FIXME: It would be nicer to have the conflict resolver - * break the move instead. It might also be a good idea to - * flag a tree conflict on each moved-away child. But doing so - * might introduce actual-only nodes without direct parents, - * and we're not yet sure if other existing code is prepared - * to handle such nodes. To be revisited post-1.8. - * - * ### In case of a conflict we are most likely creating WORKING nodes - * describing a copy of what was in BASE. The move information - * should be updated to describe a move from the WORKING layer. - * When stored that way the resolver of the tree conflict still has - * the knowledge of what was moved. + /* When deleting everything in working we should break moves from + here and to here. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE)); @@ -2338,12 +2287,12 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, iterpool = svn_pool_create(scratch_pool); while (have_row) { - const char *child_relpath; + const char *moved_to_relpath; svn_error_t *err; svn_pool_clear(iterpool); - child_relpath = svn_sqlite__column_text(stmt, 0, iterpool); - err = clear_moved_here(child_relpath, wcroot, iterpool); + moved_to_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + err = clear_moved_here(wcroot, moved_to_relpath, iterpool); if (err) return svn_error_compose_create(err, svn_sqlite__reset(stmt)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -2351,11 +2300,52 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, svn_pool_destroy(iterpool); SVN_ERR(svn_sqlite__reset(stmt)); } - if (keep_working) + else { + /* We are keeping things that are in WORKING, but we should still + break moves of things in BASE. (Mixed revisions make it + impossible to guarantee that we can keep everything moved) */ + + apr_pool_t *iterpool; + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_WORKING_BASE_DELETE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, 0)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + iterpool = svn_pool_create(scratch_pool); + while (have_row) + { + int delete_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath; + const char *dst_relpath; + svn_error_t *err; + + svn_pool_clear(iterpool); + + src_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool); + + err = svn_wc__db_op_break_move_internal(wcroot, src_relpath, + delete_op_depth, + dst_relpath, + NULL, + iterpool); + + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + svn_pool_destroy(iterpool); + SVN_ERR(svn_sqlite__reset(stmt)); + } + if (keep_working) + { + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0)); SVN_ERR(svn_sqlite__step_done(stmt)); } else @@ -2372,42 +2362,69 @@ db_base_remove(svn_wc__db_wcroot_t *wcroot, SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__step_done(stmt)); - /* Step 5: handle the BASE node itself */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_BASE_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0, - scratch_pool)); - - /* Step 6: Delete actual node if we don't keep working */ - if (! keep_working) - { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_ACTUAL_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); - } + SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool)); - if (SVN_IS_VALID_REVNUM(not_present_revision)) + if (mark_not_present || mark_excluded) { struct insert_base_baton_t ibb; - blank_ibb(&ibb); + svn_boolean_t no_marker = FALSE; - ibb.repos_id = repos_id; - ibb.status = svn_wc__db_status_not_present; - ibb.kind = kind; - ibb.repos_relpath = repos_relpath; - ibb.revision = not_present_revision; + if (file_external) + { + const char *parent_local_relpath; + const char *name; + svn_error_t *err; - /* Depending upon KIND, any of these might get used. */ - ibb.children = NULL; - ibb.depth = svn_depth_unknown; - ibb.checksum = NULL; - ibb.target = NULL; + /* For file externals we only want to place a not present marker + if there is a BASE parent */ + + svn_relpath_split(&parent_local_relpath, &name, local_relpath, + scratch_pool); - SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); + err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL, + &repos_relpath, &repos_id, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + wcroot, parent_local_relpath, + scratch_pool, scratch_pool); + + if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) + return svn_error_trace(err); + else if (err) + { + svn_error_clear(err); + no_marker = TRUE; + } + else + { + /* Replace the repos_relpath with something more expected than + the unrelated old file external repository relpath, which + one day may come from a different repository */ + repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool); + } + } + + if (!no_marker) + { + blank_ibb(&ibb); + + ibb.repos_id = repos_id; + ibb.status = mark_excluded ? svn_wc__db_status_excluded + : svn_wc__db_status_not_present; + ibb.kind = kind; + ibb.repos_relpath = repos_relpath; + ibb.revision = SVN_IS_VALID_REVNUM(marker_revision) + ? marker_revision + : revision; + + /* Depending upon KIND, any of these might get used. */ + ibb.children = NULL; + ibb.depth = svn_depth_unknown; + ibb.checksum = NULL; + ibb.target = NULL; + + SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); + } } SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); @@ -2423,9 +2440,9 @@ svn_error_t * svn_wc__db_base_remove(svn_wc__db_t *db, const char *local_abspath, svn_boolean_t keep_as_working, - svn_boolean_t queue_deletes, - svn_boolean_t remove_locks, - svn_revnum_t not_present_revision, + svn_boolean_t mark_not_present, + svn_boolean_t mark_excluded, + svn_revnum_t marker_revision, svn_skel_t *conflict, svn_skel_t *work_items, apr_pool_t *scratch_pool) @@ -2440,8 +2457,9 @@ svn_wc__db_base_remove(svn_wc__db_t *db, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath, - db, keep_as_working, queue_deletes, - remove_locks, not_present_revision, + db, keep_as_working, + mark_not_present, mark_excluded, + marker_revision, conflict, work_items, scratch_pool), wcroot); @@ -2638,7 +2656,7 @@ svn_wc__db_base_get_info(svn_wc__db_status_t *status, wcroot, local_relpath, result_pool, scratch_pool), svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, - wcroot->sdb, repos_id, result_pool), + wcroot, repos_id, result_pool), SVN_NO_ERROR, SVN_NO_ERROR, wcroot); @@ -2647,28 +2665,26 @@ svn_wc__db_base_get_info(svn_wc__db_status_t *status, return SVN_NO_ERROR; } -svn_error_t * -svn_wc__db_base_get_children_info(apr_hash_t **nodes, - svn_wc__db_t *db, - const char *dir_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +/* The implementation of svn_wc__db_base_get_children_info */ +static svn_error_t * +base_get_children_info(apr_hash_t **nodes, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t obtain_locks, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - - SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, - dir_abspath, scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); + apr_int64_t last_repos_id = INVALID_REPOS_ID; + const char *last_repos_root_url = NULL; *nodes = apr_hash_make(result_pool); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_BASE_CHILDREN_INFO)); + obtain_locks + ? STMT_SELECT_BASE_CHILDREN_INFO_LOCK + : STMT_SELECT_BASE_CHILDREN_INFO)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -2676,7 +2692,6 @@ svn_wc__db_base_get_children_info(apr_hash_t **nodes, while (have_row) { struct svn_wc__db_base_info_t *info; - svn_error_t *err; apr_int64_t repos_id; const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); const char *name = svn_relpath_basename(child_relpath, result_pool); @@ -2694,16 +2709,26 @@ svn_wc__db_base_get_children_info(apr_hash_t **nodes, info->update_root = svn_sqlite__column_boolean(stmt, 7); - info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool); + if (obtain_locks) + info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool); - err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL, - wcroot->sdb, repos_id, result_pool); + if (repos_id != last_repos_id) + { + svn_error_t *err; - if (err) - return svn_error_trace( - svn_error_compose_create(err, - svn_sqlite__reset(stmt))); + err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL, + wcroot, repos_id, + result_pool); + + if (err) + return svn_error_trace( + svn_error_compose_create(err, + svn_sqlite__reset(stmt))); + last_repos_id = repos_id; + } + + info->repos_root_url = last_repos_root_url; svn_hash_sets(*nodes, name, info); @@ -2715,6 +2740,30 @@ svn_wc__db_base_get_children_info(apr_hash_t **nodes, return SVN_NO_ERROR; } +svn_error_t * +svn_wc__db_base_get_children_info(apr_hash_t **nodes, + svn_wc__db_t *db, + const char *dir_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + dir_abspath, scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + return svn_error_trace(base_get_children_info(nodes, + wcroot, + local_relpath, + TRUE /* obtain_locks */, + result_pool, + scratch_pool)); +} + svn_error_t * svn_wc__db_base_get_props(apr_hash_t **props, @@ -2761,8 +2810,10 @@ svn_wc__db_base_get_children(const apr_array_header_t **children, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return gather_repo_children(children, wcroot, local_relpath, 0, - result_pool, scratch_pool); + return svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, 0, + result_pool, scratch_pool)); } @@ -2772,12 +2823,20 @@ svn_wc__db_base_set_dav_cache(svn_wc__db_t *db, const apr_hash_t *props, apr_pool_t *scratch_pool) { + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; svn_sqlite__stmt_t *stmt; int affected_rows; - SVN_ERR(get_statement_for_path(&stmt, db, local_abspath, - STMT_UPDATE_BASE_NODE_DAV_CACHE, - scratch_pool)); + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + local_abspath, scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_BASE_NODE_DAV_CACHE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); @@ -2799,11 +2858,20 @@ svn_wc__db_base_get_dav_cache(apr_hash_t **props, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - SVN_ERR(get_statement_for_path(&stmt, db, local_abspath, - STMT_SELECT_BASE_DAV_CACHE, scratch_pool)); + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + local_abspath, scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_BASE_DAV_CACHE)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); if (!have_row) return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, @@ -2944,21 +3012,21 @@ svn_wc__db_depth_get_info(svn_wc__db_status_t *status, } if (had_props) { - *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13); + *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 12); } if (props) { if (node_status == svn_wc__db_status_normal || node_status == svn_wc__db_status_incomplete) { - SVN_ERR(svn_sqlite__column_properties(props, stmt, 13, + SVN_ERR(svn_sqlite__column_properties(props, stmt, 12, result_pool, scratch_pool)); if (*props == NULL) *props = apr_hash_make(result_pool); } else { - assert(svn_sqlite__column_is_null(stmt, 13)); + assert(svn_sqlite__column_is_null(stmt, 12)); *props = NULL; } } @@ -2975,6 +3043,11 @@ svn_wc__db_depth_get_info(svn_wc__db_status_t *status, return svn_error_compose_create(err, svn_sqlite__reset(stmt)); } +/* A callback which supplies WCROOTs and LOCAL_RELPATHs. */ +typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool); /* Baton for passing args to with_triggers(). */ struct with_triggers_baton_t { @@ -3051,8 +3124,13 @@ with_finalization(svn_wc__db_wcroot_t *wcroot, svn_error_t *err1; svn_error_t *err2; - err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton, - scratch_pool); + err1 = svn_sqlite__begin_savepoint(wcroot->sdb); + if (!err1) + { + err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool); + + err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1); + } if (err1 == NULL && notify_func != NULL) { @@ -3438,11 +3516,18 @@ db_external_remove(const svn_skel_t *work_items, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + int affected_rows; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_EXTERNAL)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + + if (!affected_rows) + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The node '%s' is not an external."), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); @@ -3543,7 +3628,7 @@ svn_wc__db_external_read(svn_wc__db_status_t *status, err = svn_error_compose_create( err, svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, - wcroot->sdb, repos_id, + wcroot, repos_id, result_pool)); } @@ -3925,14 +4010,14 @@ get_moved_to(const char **moved_to_relpath_p, /* The body of svn_wc__db_scan_deletion(). */ static svn_error_t * -scan_deletion_txn(const char **base_del_relpath, - const char **moved_to_relpath, - const char **work_del_relpath, - const char **moved_to_op_root_relpath, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +scan_deletion(const char **base_del_relpath, + const char **moved_to_relpath, + const char **work_del_relpath, + const char **moved_to_op_root_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const char *current_relpath = local_relpath; svn_sqlite__stmt_t *stmt; @@ -3956,9 +4041,7 @@ scan_deletion_txn(const char **base_del_relpath, scan = (moved_to_op_root_relpath || moved_to_relpath); SVN_ERR(svn_sqlite__get_statement( - &stmt, wcroot->sdb, - scan ? STMT_SELECT_DELETION_INFO_SCAN - : STMT_SELECT_DELETION_INFO)); + &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -4079,6 +4162,25 @@ scan_deletion_txn(const char **base_del_relpath, } svn_error_t * +svn_wc__db_scan_deletion_internal( + const char **base_del_relpath, + const char **moved_to_relpath, + const char **work_del_relpath, + const char **moved_to_op_root_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + return svn_error_trace( + scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath, + moved_to_op_root_relpath, + wcroot, local_relpath, + result_pool, scratch_pool)); +} + + +svn_error_t * svn_wc__db_scan_deletion(const char **base_del_abspath, const char **moved_to_abspath, const char **work_del_abspath, @@ -4100,9 +4202,9 @@ svn_wc__db_scan_deletion(const char **base_del_abspath, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - scan_deletion_txn(&base_del_relpath, &moved_to_relpath, - &work_del_relpath, &moved_to_op_root_relpath, - wcroot, local_relpath, result_pool, scratch_pool), + scan_deletion(&base_del_relpath, &moved_to_relpath, + &work_del_relpath, &moved_to_op_root_relpath, + wcroot, local_relpath, result_pool, scratch_pool), wcroot); if (base_del_abspath) @@ -4200,10 +4302,10 @@ get_info_for_copy(apr_int64_t *copyfrom_id, { const char *base_del_relpath, *work_del_relpath; - SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL, - &work_del_relpath, - NULL, src_wcroot, local_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(scan_deletion(&base_del_relpath, NULL, + &work_del_relpath, + NULL, src_wcroot, local_relpath, + scratch_pool, scratch_pool)); if (work_del_relpath) { const char *op_root_relpath; @@ -4264,7 +4366,7 @@ get_info_for_copy(apr_int64_t *copyfrom_id, working copies in a single db)! */ SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - src_wcroot->sdb, *copyfrom_id, + src_wcroot, *copyfrom_id, scratch_pool)); SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid, @@ -4532,8 +4634,9 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcroot, int src_op_depth; SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath)); - SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath, - src_op_depth, scratch_pool, scratch_pool)); + SVN_ERR(gather_children(&children, src_wcroot, src_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth, + scratch_pool, scratch_pool)); } else children = NULL; @@ -4684,25 +4787,23 @@ struct op_copy_baton const char *dst_op_root_relpath; }; -/* Helper for svn_wc__db_op_copy(). - * - * Implements svn_sqlite__transaction_callback_t. */ +/* Helper for svn_wc__db_op_copy(). */ static svn_error_t * -op_copy_txn(void * baton, - svn_sqlite__db_t *sdb, +op_copy_txn(svn_wc__db_wcroot_t *wcroot, + struct op_copy_baton *ocb, apr_pool_t *scratch_pool) { - struct op_copy_baton *ocb = baton; int move_op_depth; - if (sdb != ocb->dst_wcroot->sdb) + if (wcroot != ocb->dst_wcroot) { /* Source and destination databases differ; so also start a lock - in the destination database, by calling ourself in a lock. */ + in the destination database, by calling ourself in an extra lock. */ + + SVN_WC__DB_WITH_TXN(op_copy_txn(ocb->dst_wcroot, ocb, scratch_pool), + ocb->dst_wcroot); - return svn_error_trace( - svn_sqlite__with_lock(ocb->dst_wcroot->sdb, - op_copy_txn, ocb, scratch_pool)); + return SVN_NO_ERROR; } /* From this point we can assume a lock in the src and dst databases */ @@ -4753,8 +4854,237 @@ svn_wc__db_op_copy(svn_wc__db_t *db, /* Call with the sdb in src_wcroot. It might call itself again to also obtain a lock in dst_wcroot */ - SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb, - scratch_pool)); + SVN_WC__DB_WITH_TXN(op_copy_txn(ocb.src_wcroot, &ocb, scratch_pool), + ocb.src_wcroot); + + return SVN_NO_ERROR; +} + +/* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */ +static svn_error_t * +clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row, shadowed; + svn_boolean_t keep_conflict = FALSE; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_NODE_INFO)); + + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + { + svn_wc__db_status_t presence; + + shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth); + presence = svn_sqlite__column_token(stmt, 3, presence_map); + + if (shadowed && presence == svn_wc__db_status_base_deleted) + { + keep_conflict = TRUE; + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + if (have_row) + shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth); + else + shadowed = FALSE; + } + } + else + shadowed = FALSE; + + SVN_ERR(svn_sqlite__reset(stmt)); + if (shadowed) + return SVN_NO_ERROR; + + if (keep_conflict) + { + /* We don't want to accidentally remove delete-delete conflicts */ + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_EMPTY)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + else + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_op_relpath, + int src_op_depth, + const char *dst_op_relpath, + svn_skel_t *conflict, + svn_skel_t *work_items, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt, *stmt2; + svn_boolean_t have_row; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int dst_op_depth = relpath_depth(dst_op_relpath); + svn_boolean_t locked; + svn_error_t *err = NULL; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath, + FALSE, scratch_pool)); + + if (!locked) + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("No write-lock in '%s'"), + path_for_error_message(wcroot, dst_op_relpath, + scratch_pool)); + + SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb, + STMT_COPY_NODE_MOVE)); + + /* Replace entire subtree at one op-depth. */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_LAYER_FOR_REPLACE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id, + src_op_relpath, src_op_depth, + dst_op_relpath, dst_op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + while (have_row) + { + const char *src_relpath; + const char *dst_relpath; + + svn_pool_clear(iterpool); + + src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); + dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool); + + err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id, + src_relpath, src_op_depth, + dst_relpath, dst_op_depth, + svn_relpath_dirname(dst_relpath, iterpool)); + if (!err) + err = svn_sqlite__step_done(stmt2); + + /* stmt2 is reset (never modified or by step_done) */ + + if (err) + break; + + /* The node can't be deleted where it is added, so extension of + an existing shadowing is only interesting 2 levels deep. */ + if (relpath_depth(dst_relpath) > (dst_op_depth+1)) + { + svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3); + + if (exists) + { + svn_wc__db_status_t presence; + + presence = svn_sqlite__column_token(stmt, 3, presence_map); + + if (presence != svn_wc__db_status_normal) + exists = FALSE; + } + + if (!exists) + { + svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map); + + err = db_extend_parent_delete(wcroot, dst_relpath, + kind, dst_op_depth, iterpool); + + if (err) + break; + } + } + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + + /* And now remove the records that are no longer needed */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_NO_LONGER_MOVED_RV)); + SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id, + dst_op_relpath, dst_op_depth, + src_op_relpath, src_op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + while (have_row) + { + const char *dst_relpath; + svn_wc__db_status_t shadowed_presence; + + svn_pool_clear(iterpool); + + dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool); + + if (!svn_sqlite__column_is_null(stmt, 2)) + shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map); + else + shadowed_presence = svn_wc__db_status_not_present; + + if (shadowed_presence != svn_wc__db_status_normal + && shadowed_presence != svn_wc__db_status_incomplete) + { + err = svn_sqlite__get_statement(&stmt2, wcroot->sdb, + STMT_DELETE_NODE); + } + else + { + err =svn_sqlite__get_statement(&stmt2, wcroot->sdb, + STMT_REPLACE_WITH_BASE_DELETED); + } + + if (!err) + err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath, + dst_op_depth); + + if (!err) + err = svn_sqlite__step_done(stmt2); + + /* stmt2 is reset (never modified or by step_done) */ + if (err) + break; + + /* Delete ACTUAL information about this node that we just deleted */ + err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth, + scratch_pool); + + if (err) + break; + + /* Retract base-delete for the node itself */ + err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth, + scratch_pool); + + if (err) + break; + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + svn_pool_destroy(iterpool); + + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + + SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); + + if (conflict) + SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */, + conflict, scratch_pool)); return SVN_NO_ERROR; } @@ -4919,7 +5249,7 @@ handle_move_back(svn_boolean_t *moved_back, generally these values should be the same anyway as it was a no-op move. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_MOVED_BACK)); + STMT_DELETE_WORKING_OP_DEPTH)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, @@ -5044,7 +5374,7 @@ db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot, const char *repos_uuid; SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - src_wcroot->sdb, node_repos_id, + src_wcroot, node_repos_id, scratch_pool)); SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid, @@ -5166,8 +5496,9 @@ db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot, return SVN_NO_ERROR; } - SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath, - src_op_depth, scratch_pool, iterpool)); + SVN_ERR(gather_children(&children, src_wcroot, src_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth, + scratch_pool, iterpool)); for (i = 0; i < children->nelts; i++) { @@ -5196,15 +5527,12 @@ db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot, return SVN_NO_ERROR; } -/* Helper for svn_wc__db_op_copy_shadowed_layer(). - * - * Implements svn_sqlite__transaction_callback_t. */ +/* Helper for svn_wc__db_op_copy_shadowed_layer(). */ static svn_error_t * -op_copy_shadowed_layer_txn(void *baton, - svn_sqlite__db_t *sdb, +op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t *wcroot, + struct op_copy_baton *ocb, apr_pool_t *scratch_pool) { - struct op_copy_baton *ocb = baton; const char *src_parent_relpath; const char *dst_parent_relpath; int src_op_depth; @@ -5214,15 +5542,16 @@ op_copy_shadowed_layer_txn(void *baton, apr_int64_t repos_id = INVALID_REPOS_ID; svn_revnum_t revision = SVN_INVALID_REVNUM; - if (sdb != ocb->dst_wcroot->sdb) + if (wcroot != ocb->dst_wcroot) { - /* Source and destination databases differ; so also start a lock - in the destination database, by calling ourself in a lock. */ + /* Source and destination databases differ; so also start a lock + in the destination database, by calling ourself in an extra lock. */ - return svn_error_trace( - svn_sqlite__with_lock(ocb->dst_wcroot->sdb, - op_copy_shadowed_layer_txn, - ocb, scratch_pool)); + SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb->dst_wcroot, ocb, + scratch_pool), + ocb->dst_wcroot); + + return SVN_NO_ERROR; } /* From this point we can assume a lock in the src and dst databases */ @@ -5304,9 +5633,9 @@ svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db, /* Call with the sdb in src_wcroot. It might call itself again to also obtain a lock in dst_wcroot */ - SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, - op_copy_shadowed_layer_txn, - &ocb, scratch_pool)); + SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb.src_wcroot, &ocb, + scratch_pool), + ocb.src_wcroot); return SVN_NO_ERROR; } @@ -5776,27 +6105,39 @@ svn_wc__db_global_record_fileinfo(svn_wc__db_t *db, * props; to indicate no properties when the pristine has some props, * PROPS must be an empty hash. */ static svn_error_t * -set_actual_props(apr_int64_t wc_id, +set_actual_props(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_hash_t *props, - svn_sqlite__db_t *db, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; int affected_rows; - SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); if (affected_rows == 1 || !props) - return SVN_NO_ERROR; /* We are done */ + { + /* Perhaps the entire ACTUAL record is unneeded now? */ + if (!props && affected_rows) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_EMPTY)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + + return SVN_NO_ERROR; /* We are done */ + } /* We have to insert a row in ACTUAL */ - SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_INSERT_ACTUAL_PROPS)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); if (*local_relpath != '\0') SVN_ERR(svn_sqlite__bind_text(stmt, 3, svn_relpath_dirname(local_relpath, @@ -5805,6 +6146,24 @@ set_actual_props(apr_int64_t wc_id, return svn_error_trace(svn_sqlite__step_done(stmt)); } +svn_error_t * +svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_hash_t *props, + svn_boolean_t clear_recorded_info, + apr_pool_t *scratch_pool) +{ + SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool)); + + if (clear_recorded_info) + { + SVN_ERR(db_record_fileinfo(wcroot, local_relpath, + SVN_INVALID_FILESIZE, 0, + scratch_pool)); + } + + return SVN_NO_ERROR; +} /* The body of svn_wc__db_op_set_props(). @@ -5840,15 +6199,8 @@ set_props_txn(svn_wc__db_wcroot_t *wcroot, props = NULL; } - SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, - props, wcroot->sdb, scratch_pool)); - - if (clear_recorded_info) - { - SVN_ERR(db_record_fileinfo(wcroot, local_relpath, - SVN_INVALID_FILESIZE, 0, - scratch_pool)); - } + SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath, props, + clear_recorded_info, scratch_pool)); /* And finally. */ SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); @@ -6080,7 +6432,7 @@ set_changelist_txn(void *baton, if (scb->new_changelist) { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_ACTUAL_EMPTIES)); + STMT_INSERT_ACTUAL_EMPTIES_FILES)); SVN_ERR(svn_sqlite__step_done(stmt)); } @@ -6297,15 +6649,15 @@ svn_wc__db_op_mark_conflict(svn_wc__db_t *db, /* The body of svn_wc__db_op_mark_resolved(). */ -static svn_error_t * -db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_wc__db_t *db, - svn_boolean_t resolved_text, - svn_boolean_t resolved_props, - svn_boolean_t resolved_tree, - const svn_skel_t *work_items, - apr_pool_t *scratch_pool) +svn_error_t * +svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_wc__db_t *db, + svn_boolean_t resolved_text, + svn_boolean_t resolved_props, + svn_boolean_t resolved_tree, + const svn_skel_t *work_items, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; @@ -6403,7 +6755,8 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t *db, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - db_op_mark_resolved(wcroot, local_relpath, db, + svn_wc__db_op_mark_resolved_internal( + wcroot, local_relpath, db, resolved_text, resolved_props, resolved_tree, work_items, scratch_pool), wcroot); @@ -6412,40 +6765,116 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t *db, return SVN_NO_ERROR; } -/* Clear moved-to information at the delete-half of the move which - * moved LOCAL_RELPATH here. This transforms the move into a simple delete. */ +/* Clear moved-to information at the delete-half of the move which moved + * MOVED_TO_RELPATH here. This transforms the delete part of the move into a + * normal delete. + * + * Note that the moved-to location is always an op-root, while this is not the + * case for a moved-from location. + */ static svn_error_t * -clear_moved_to(const char *local_relpath, - svn_wc__db_wcroot_t *wcroot, +clear_moved_to(svn_wc__db_wcroot_t *wcroot, + const char *moved_to_relpath, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; const char *moved_from_relpath; + int moved_from_op_depth; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_FROM_RELPATH)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (!have_row) - { - SVN_ERR(svn_sqlite__reset(stmt)); - return SVN_NO_ERROR; - } + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, moved_to_relpath)); + SVN_ERR(svn_sqlite__step_row(stmt)); moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); + moved_from_op_depth = svn_sqlite__column_int(stmt, 1); SVN_ERR(svn_sqlite__reset(stmt)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_CLEAR_MOVED_TO_RELPATH)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - moved_from_relpath, - relpath_depth(moved_from_relpath))); - SVN_ERR(svn_sqlite__step_done(stmt)); + moved_from_relpath, moved_from_op_depth)); + SVN_ERR(svn_sqlite__update(NULL, stmt)); + + return SVN_NO_ERROR; +} + +/* Helper function for op_revert_txn. Raises move tree conflicts on + descendants to ensure database stability on a non recursive revert + of an ancestor that contains a possible move related tree conflict. + */ +static svn_error_t * +revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot, + svn_wc__db_t *db, + const char *local_relpath, + int op_depth_below, + apr_pool_t *scratch_pool) +{ + svn_skel_t *conflict; + svn_wc_operation_t operation; + svn_boolean_t tree_conflicted; + const apr_array_header_t *locations; + svn_wc_conflict_reason_t reason; + svn_wc_conflict_action_t action; + + SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot, + local_relpath, + scratch_pool, scratch_pool)); + + if (!conflict) + return SVN_NO_ERROR; + + SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, NULL, NULL, + &tree_conflicted, + db, wcroot->abspath, + conflict, + scratch_pool, scratch_pool)); + + if (!tree_conflicted + || (operation != svn_wc_operation_update + && operation != svn_wc_operation_switch)) + { + return SVN_NO_ERROR; + } + + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, + NULL, + db, wcroot->abspath, + conflict, + scratch_pool, + scratch_pool)); + + if (reason == svn_wc_conflict_reason_deleted + || reason == svn_wc_conflict_reason_replaced) + { + SVN_ERR(svn_wc__db_op_raise_moved_away_internal( + wcroot, local_relpath, op_depth_below, db, + operation, action, + (locations && locations->nelts > 0) + ? APR_ARRAY_IDX(locations, 0, + const svn_wc_conflict_version_t *) + : NULL, + (locations && locations->nelts > 1) + ? APR_ARRAY_IDX(locations, 1, + const svn_wc_conflict_version_t *) + : NULL, + scratch_pool)); + + /* Transform the move information into revert information */ + SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, + STMT_MOVE_NOTIFY_TO_REVERT)); + } return SVN_NO_ERROR; } +/* Baton for op_revert_txn and op_revert_recursive_txn */ +struct revert_baton_t +{ + svn_wc__db_t *db; + svn_boolean_t clear_changelists; +}; + /* One of the two alternative bodies of svn_wc__db_op_revert(). * * Implements svn_wc__db_txn_callback_t. */ @@ -6455,13 +6884,15 @@ op_revert_txn(void *baton, const char *local_relpath, apr_pool_t *scratch_pool) { - svn_wc__db_t *db = baton; + struct revert_baton_t *rvb = baton; + svn_wc__db_t *db = rvb->db; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; int op_depth; svn_boolean_t moved_here; int affected_rows; const char *moved_to; + int op_depth_below; /* ### Similar structure to op_revert_recursive_txn, should they be combined? */ @@ -6508,58 +6939,26 @@ op_revert_txn(void *baton, op_depth = svn_sqlite__column_int(stmt, 0); moved_here = svn_sqlite__column_boolean(stmt, 15); moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool); - SVN_ERR(svn_sqlite__reset(stmt)); - if (moved_to) - { - SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot, - local_relpath, - op_depth, - scratch_pool)); - } + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + op_depth_below = svn_sqlite__column_int(stmt, 0); else - { - svn_skel_t *conflict; + op_depth_below = -1; - SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot, - local_relpath, - scratch_pool, scratch_pool)); - if (conflict) - { - svn_wc_operation_t operation; - svn_boolean_t tree_conflicted; - - SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL, - &tree_conflicted, - db, wcroot->abspath, - conflict, - scratch_pool, scratch_pool)); - if (tree_conflicted - && (operation == svn_wc_operation_update - || operation == svn_wc_operation_switch)) - { - svn_wc_conflict_reason_t reason; - svn_wc_conflict_action_t action; - - SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, - NULL, - db, wcroot->abspath, - conflict, - scratch_pool, - scratch_pool)); + SVN_ERR(svn_sqlite__reset(stmt)); - if (reason == svn_wc_conflict_reason_deleted) - SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away( - db, svn_dirent_join(wcroot->abspath, local_relpath, - scratch_pool), - NULL, NULL /* ### How do we notify this? */, - scratch_pool)); - } - } + if (moved_to) + { + SVN_ERR(svn_wc__db_op_break_move_internal(wcroot, + local_relpath, op_depth, + moved_to, NULL, scratch_pool)); } if (op_depth > 0 && op_depth == relpath_depth(local_relpath)) { + int op_depth_increased; + /* Can't do non-recursive revert if children exist */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_GE_OP_DEPTH_CHILDREN)); @@ -6582,7 +6981,7 @@ op_revert_txn(void *baton, SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__update(&op_depth_increased, stmt)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WORKING_NODE)); @@ -6597,19 +6996,35 @@ op_revert_txn(void *baton, /* If this node was moved-here, clear moved-to at the move source. */ if (moved_here) - SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool)); + SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool)); + + /* If the node was moved itself, we don't have interesting moved + children (and the move itself was already broken) */ + if (op_depth_increased && !moved_to) + SVN_ERR(revert_maybe_raise_moved_away(wcroot, db, local_relpath, + op_depth_below, scratch_pool)); } - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); - if (!affected_rows) + if (rvb->clear_changelists) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + } + else { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST)); + STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + if (!affected_rows) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + } } return SVN_NO_ERROR; @@ -6625,6 +7040,7 @@ op_revert_recursive_txn(void *baton, const char *local_relpath, apr_pool_t *scratch_pool) { + struct revert_baton_t *rvb = baton; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; int op_depth; @@ -6680,14 +7096,14 @@ op_revert_recursive_txn(void *baton, SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { - const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL); + const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL); + const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL); int move_op_depth = svn_sqlite__column_int(stmt, 2); svn_error_t *err; - err = svn_wc__db_resolve_break_moved_away_internal(wcroot, - move_src_relpath, - move_op_depth, - scratch_pool); + err = svn_wc__db_op_break_move_internal(wcroot, + src_relpath, move_op_depth, + dst_relpath, NULL, scratch_pool); if (err) return svn_error_compose_create(err, svn_sqlite__reset(stmt)); @@ -6706,17 +7122,28 @@ op_revert_recursive_txn(void *baton, local_relpath, select_op_depth)); SVN_ERR(svn_sqlite__step_done(stmt)); - SVN_ERR(svn_sqlite__get_statement( - &stmt, wcroot->sdb, - STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); + if (rvb->clear_changelists) + { + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_NODE_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + else + { + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); - SVN_ERR(svn_sqlite__get_statement( - &stmt, wcroot->sdb, - STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } /* ### This removes the locks, but what about the access batons? */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -6740,7 +7167,7 @@ op_revert_recursive_txn(void *baton, svn_pool_clear(iterpool); moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool); - err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool); + err = clear_moved_to(wcroot, moved_here_child_relpath, iterpool); if (err) return svn_error_trace(svn_error_compose_create( err, @@ -6754,7 +7181,7 @@ op_revert_recursive_txn(void *baton, /* Clear potential moved-to pointing at the target node itself. */ if (op_depth > 0 && op_depth == relpath_depth(local_relpath) && moved_here) - SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool)); + SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool)); return SVN_NO_ERROR; } @@ -6763,22 +7190,27 @@ svn_error_t * svn_wc__db_op_revert(svn_wc__db_t *db, const char *local_abspath, svn_depth_t depth, + svn_boolean_t clear_changelists, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; + struct revert_baton_t rvb; struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST, STMT_DROP_REVERT_LIST_TRIGGERS, NULL, NULL}; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + rvb.db = db; + rvb.clear_changelists = clear_changelists; + wtb.cb_baton = &rvb; + switch (depth) { case svn_depth_empty: wtb.cb_func = op_revert_txn; - wtb.cb_baton = db; break; case svn_depth_infinity: wtb.cb_func = op_revert_recursive_txn; @@ -6914,7 +7346,7 @@ svn_wc__db_revert_list_read(svn_boolean_t *reverted, static svn_error_t * revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - const apr_array_header_t **children_p, + apr_array_header_t **children_p, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -6957,7 +7389,7 @@ revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot, svn_error_t * -svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children, +svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -7004,16 +7436,39 @@ svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func, while (have_row) { const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL); + svn_wc_notify_t *notify; svn_pool_clear(iterpool); - notify_func(notify_baton, - svn_wc_create_notify(svn_dirent_join(wcroot->abspath, - notify_relpath, - iterpool), - svn_wc_notify_revert, - iterpool), - iterpool); + notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath, + notify_relpath, + iterpool), + svn_wc_notify_revert, + iterpool); + + if (!svn_sqlite__column_is_null(stmt, 1)) + notify->kind = svn_sqlite__column_token(stmt, 1, kind_map); + else + { + if (!svn_sqlite__column_is_null(stmt, 3)) + notify->kind = svn_sqlite__column_token(stmt, 3, kind_map_none); + + switch (svn_sqlite__column_int(stmt, 2)) + { + case 0: + continue; + case 1: + /* standard revert */ + break; + case 2: + notify->action = svn_wc_notify_tree_conflict; + break; + default: + SVN_ERR_MALFUNCTION(); + } + } + + notify_func(notify_baton, notify, iterpool); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } @@ -7055,9 +7510,6 @@ remove_node_txn(svn_boolean_t *left_changes, svn_wc__db_t *db, svn_boolean_t destroy_wc, svn_boolean_t destroy_changes, - svn_revnum_t not_present_rev, - svn_wc__db_status_t not_present_status, - svn_node_kind_t not_present_kind, const svn_skel_t *conflict, const svn_skel_t *work_items, svn_cancel_func_t cancel_func, @@ -7066,9 +7518,6 @@ remove_node_txn(svn_boolean_t *left_changes, { svn_sqlite__stmt_t *stmt; - apr_int64_t repos_id; - const char *repos_relpath; - /* Note that unlike many similar functions it is a valid scenario for this function to be called on a wcroot! */ @@ -7078,15 +7527,6 @@ remove_node_txn(svn_boolean_t *left_changes, if (left_changes) *left_changes = FALSE; - /* Need info for not_present node? */ - if (SVN_IS_VALID_REVNUM(not_present_rev)) - SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, - &repos_relpath, &repos_id, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - if (destroy_wc && (!destroy_changes || *local_relpath == '\0')) { @@ -7283,26 +7723,6 @@ remove_node_txn(svn_boolean_t *left_changes, local_relpath)); SVN_ERR(svn_sqlite__step_done(stmt)); - /* Should we leave a not-present node? */ - if (SVN_IS_VALID_REVNUM(not_present_rev)) - { - insert_base_baton_t ibb; - blank_ibb(&ibb); - - ibb.repos_id = repos_id; - - SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present - || not_present_status == svn_wc__db_status_excluded); - - ibb.status = not_present_status; - ibb.kind = not_present_kind; - - ibb.repos_relpath = repos_relpath; - ibb.revision = not_present_rev; - - SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); - } - SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); if (conflict) SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, @@ -7317,9 +7737,6 @@ svn_wc__db_op_remove_node(svn_boolean_t *left_changes, const char *local_abspath, svn_boolean_t destroy_wc, svn_boolean_t destroy_changes, - svn_revnum_t not_present_revision, - svn_wc__db_status_t not_present_status, - svn_node_kind_t not_present_kind, const svn_skel_t *conflict, const svn_skel_t *work_items, svn_cancel_func_t cancel_func, @@ -7338,8 +7755,7 @@ svn_wc__db_op_remove_node(svn_boolean_t *left_changes, SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes, wcroot, local_relpath, db, destroy_wc, destroy_changes, - not_present_revision, not_present_status, - not_present_kind, conflict, work_items, + conflict, work_items, cancel_func, cancel_baton, scratch_pool), wcroot); @@ -7371,7 +7787,7 @@ db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot, if (affected_rows == 0) return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, - "The node '%s' is not a committed directory", + _("The node '%s' is not a committed directory"), path_for_error_message(wcroot, local_relpath, scratch_pool)); @@ -8109,7 +8525,7 @@ struct op_delete_many_baton_t { apr_array_header_t *rel_targets; svn_boolean_t delete_dir_externals; const svn_skel_t *work_items; -} op_delete_many_baton_t; +}; static svn_error_t * op_delete_many_txn(void *baton, @@ -8344,6 +8760,45 @@ svn_wc__db_op_delete_many(svn_wc__db_t *db, scratch_pool)); } +/* Helper function for read_info() to provide better diagnostics than just + asserting. + + ### BH: Yes this code is ugly, and that is why I only introduce it in + ### read_info(). But we really need something to determine the root cause + ### of this problem to diagnose why TortoiseSVN users were seeing all those + ### assertions. + + Adds an error to the *err chain if invalid values are encountered. In that + case the value is set to the first value in the map, assuming that caller + will just return the combined error. + */ +static int +column_token_err(svn_error_t **err, + svn_sqlite__stmt_t *stmt, + int column, + const svn_token_map_t *map) +{ + svn_error_t *err2; + const char *word = svn_sqlite__column_text(stmt, column, NULL); + int value; + + /* svn_token__from_word_err() handles NULL for us */ + err2 = svn_token__from_word_err(&value, map, word); + + if (err2) + { + *err = svn_error_compose_create( + *err, + svn_error_createf( + SVN_ERR_WC_CORRUPT, err2, + _("Encountered invalid node state in column %d of " + "info query to working copy database"), + column)); + value = map[0].val; + } + + return value; +} /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */ @@ -8411,11 +8866,11 @@ read_info(svn_wc__db_status_t *status, svn_node_kind_t node_kind; op_depth = svn_sqlite__column_int(stmt_info, 0); - node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map); + node_kind = column_token_err(&err, stmt_info, 4, kind_map); if (status) { - *status = svn_sqlite__column_token(stmt_info, 3, presence_map); + *status = column_token_err(&err, stmt_info, 3, presence_map); if (op_depth != 0) /* WORKING */ err = svn_error_compose_create(err, @@ -8467,14 +8922,11 @@ read_info(svn_wc__db_status_t *status, if (depth) { if (node_kind != svn_node_dir) - { - *depth = svn_depth_unknown; - } + *depth = svn_depth_unknown; + else if (svn_sqlite__column_is_null(stmt_info, 11)) + *depth = svn_depth_unknown; else - { - *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map, - svn_depth_unknown); - } + *depth = column_token_err(&err, stmt_info, 11, depth_map); } if (checksum) { @@ -8666,10 +9118,8 @@ read_info(svn_wc__db_status_t *status, err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act)); if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) - err = svn_error_quick_wrap(err, - apr_psprintf(scratch_pool, - "Error reading node '%s'", - local_relpath)); + err = svn_error_quick_wrapf(err, _("Error reading node '%s'"), + local_relpath); SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info))); @@ -8773,9 +9223,9 @@ svn_wc__db_read_info(svn_wc__db_status_t *status, have_base, have_more_work, have_work, wcroot, local_relpath, result_pool, scratch_pool), svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, - wcroot->sdb, repos_id, result_pool), + wcroot, repos_id, result_pool), svn_wc__db_fetch_repos_info(original_root_url, original_uuid, - wcroot->sdb, original_repos_id, + wcroot, original_repos_id, result_pool), SVN_NO_ERROR, wcroot); @@ -8789,6 +9239,27 @@ is_wclocked(svn_boolean_t *locked, const char *dir_relpath, apr_pool_t *scratch_pool); +/* Helper for read_children_info and single variant */ +static svn_error_t * +find_conflict_descendants(svn_boolean_t *conflict_exists, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + + /* Only used on files, so certainly not wcroot*/ + assert(local_relpath[0] != '\0'); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_FIND_CONFLICT_DESCENDANT)); + + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(conflict_exists, stmt)); + + return svn_error_trace(svn_sqlite__reset(stmt)); +} + /* What we really want to store about a node. This relies on the offset of svn_wc__db_info_t being zero. */ struct read_children_info_item_t @@ -8796,13 +9267,16 @@ struct read_children_info_item_t struct svn_wc__db_info_t info; int op_depth; int nr_layers; + svn_boolean_t was_dir; }; +/* Implementation of svn_wc__db_read_children_info */ static svn_error_t * read_children_info(svn_wc__db_wcroot_t *wcroot, const char *dir_relpath, apr_hash_t *conflicts, apr_hash_t *nodes, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -8811,9 +9285,12 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, const char *repos_root_url = NULL; const char *repos_uuid = NULL; apr_int64_t last_repos_id = INVALID_REPOS_ID; + const char *last_repos_root_url = NULL; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_NODE_CHILDREN_INFO)); + (base_tree_only + ? STMT_SELECT_BASE_NODE_CHILDREN_INFO + : STMT_SELECT_NODE_CHILDREN_INFO))); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); @@ -8828,7 +9305,7 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, int op_depth; svn_boolean_t new_child; - child_item = svn_hash_gets(nodes, name); + child_item = (base_tree_only ? NULL : svn_hash_gets(nodes, name)); if (child_item) new_child = FALSE; else @@ -8840,7 +9317,7 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, op_depth = svn_sqlite__column_int(stmt, 0); /* Do we have new or better information? */ - if (new_child || op_depth > child_item->op_depth) + if (new_child) { struct svn_wc__db_info_t *child = &child_item->info; child_item->op_depth = op_depth; @@ -8875,8 +9352,6 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, } else { - const char *last_repos_root_url = NULL; - apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1); if (!repos_root_url || (last_repos_id != INVALID_REPOS_ID && @@ -8885,7 +9360,7 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, last_repos_root_url = repos_root_url; err = svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - wcroot->sdb, repos_id, + wcroot, repos_id, result_pool); if (err) SVN_ERR(svn_error_compose_create(err, @@ -8923,11 +9398,19 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, child->depth = svn_depth_unknown; else { + child->has_descendants = TRUE; + child_item->was_dir = TRUE; child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map, svn_depth_unknown); if (new_child) - SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath, - scratch_pool)); + { + err = is_wclocked(&child->locked, wcroot, child_relpath, + scratch_pool); + + if (err) + SVN_ERR(svn_error_compose_create(err, + svn_sqlite__reset(stmt))); + } } child->recorded_time = svn_sqlite__column_int64(stmt, 13); @@ -8959,6 +9442,17 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, if (new_child) svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child); } + else if (!child_item->was_dir + && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir) + { + child_item->was_dir = TRUE; + + err = find_conflict_descendants(&child_item->info.has_descendants, + wcroot, child_relpath, + scratch_pool); + if (err) + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + } if (op_depth == 0) { @@ -8990,22 +9484,14 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, struct svn_wc__db_moved_to_info_t *moved_to; struct svn_wc__db_moved_to_info_t **next; const char *shadow_op_relpath; - int cur_op_depth; moved_to = apr_pcalloc(result_pool, sizeof(*moved_to)); moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool); - cur_op_depth = relpath_depth(child_relpath); - shadow_op_relpath = child_relpath; - - while (cur_op_depth > op_depth) - { - shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath, - scratch_pool); - cur_op_depth--; - } + shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth, + scratch_pool); moved_to->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath, shadow_op_relpath, @@ -9028,54 +9514,58 @@ read_children_info(svn_wc__db_wcroot_t *wcroot, SVN_ERR(svn_sqlite__reset(stmt)); - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_ACTUAL_CHILDREN_INFO)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - - while (have_row) + if (!base_tree_only) { - struct read_children_info_item_t *child_item; - struct svn_wc__db_info_t *child; - const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *name = svn_relpath_basename(child_relpath, NULL); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_ACTUAL_CHILDREN_INFO)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); - child_item = svn_hash_gets(nodes, name); - if (!child_item) + while (have_row) { - child_item = apr_pcalloc(result_pool, sizeof(*child_item)); - child_item->info.status = svn_wc__db_status_not_present; - } + struct read_children_info_item_t *child_item; + struct svn_wc__db_info_t *child; + const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); + const char *name = svn_relpath_basename(child_relpath, NULL); + + child_item = svn_hash_gets(nodes, name); + if (!child_item) + { + child_item = apr_pcalloc(result_pool, sizeof(*child_item)); + child_item->info.status = svn_wc__db_status_not_present; + } - child = &child_item->info; + child = &child_item->info; - child->changelist = svn_sqlite__column_text(stmt, 1, result_pool); + child->changelist = svn_sqlite__column_text(stmt, 1, result_pool); - child->props_mod = !svn_sqlite__column_is_null(stmt, 2); + child->props_mod = !svn_sqlite__column_is_null(stmt, 2); #ifdef HAVE_SYMLINK - if (child->props_mod) - { - svn_error_t *err; - apr_hash_t *properties; + if (child->props_mod) + { + svn_error_t *err; + apr_hash_t *properties; - err = svn_sqlite__column_properties(&properties, stmt, 2, - scratch_pool, scratch_pool); - if (err) - SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); - child->special = (NULL != svn_hash_gets(properties, - SVN_PROP_SPECIAL)); - } + err = svn_sqlite__column_properties(&properties, stmt, 2, + scratch_pool, scratch_pool); + if (err) + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + child->special = (NULL != svn_hash_gets(properties, + SVN_PROP_SPECIAL)); + } #endif - child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */ + /* conflict */ + child->conflicted = !svn_sqlite__column_is_null(stmt, 3); - if (child->conflicted) - svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), ""); + if (child->conflicted) + svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), ""); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + } return SVN_NO_ERROR; } @@ -9085,6 +9575,7 @@ svn_wc__db_read_children_info(apr_hash_t **nodes, apr_hash_t **conflicts, svn_wc__db_t *db, const char *dir_abspath, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -9102,23 +9593,29 @@ svn_wc__db_read_children_info(apr_hash_t **nodes, SVN_WC__DB_WITH_TXN( read_children_info(wcroot, dir_relpath, *conflicts, *nodes, - result_pool, scratch_pool), + base_tree_only, result_pool, scratch_pool), wcroot); return SVN_NO_ERROR; } -static svn_error_t * -db_read_props(apr_hash_t **props, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); +/* Implementation of svn_wc__db_read_single_info. + ### This function is very similar to a lot of code inside + read_children_info, but that performs some tricks to re-use data between + different siblings. + + ### We read the same few NODES records a few times via different helper + functions, so this could be optimized bit, but everything is within + a sqlite transaction and all queries are backed by an index, so generally + everything (including the used indexes) should be in the sqlite page cache + after the first query. +*/ static svn_error_t * read_single_info(const struct svn_wc__db_info_t **info, svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -9127,27 +9624,43 @@ read_single_info(const struct svn_wc__db_info_t **info, const svn_checksum_t *checksum; const char *original_repos_relpath; svn_boolean_t have_work; + apr_hash_t *properties; mtb = apr_pcalloc(result_pool, sizeof(*mtb)); - SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum, - &mtb->repos_relpath, &repos_id, &mtb->changed_rev, - &mtb->changed_date, &mtb->changed_author, &mtb->depth, - &checksum, NULL, &original_repos_relpath, NULL, NULL, - &mtb->lock, &mtb->recorded_size, &mtb->recorded_time, - &mtb->changelist, &mtb->conflicted, &mtb->op_root, - &mtb->had_props, &mtb->props_mod, &mtb->have_base, - &mtb->have_more_work, &have_work, - wcroot, local_relpath, - result_pool, scratch_pool)); + if (!base_tree_only) + SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum, + &mtb->repos_relpath, &repos_id, &mtb->changed_rev, + &mtb->changed_date, &mtb->changed_author, &mtb->depth, + &checksum, NULL, &original_repos_relpath, NULL, NULL, + &mtb->lock, &mtb->recorded_size, &mtb->recorded_time, + &mtb->changelist, &mtb->conflicted, &mtb->op_root, + &mtb->had_props, &mtb->props_mod, &mtb->have_base, + &mtb->have_more_work, &have_work, + wcroot, local_relpath, result_pool, scratch_pool)); + else + { + svn_boolean_t update_root; + + have_work = FALSE; + original_repos_relpath = NULL; + + SVN_ERR(svn_wc__db_base_get_info_internal( + &mtb->status, &mtb->kind, &mtb->revnum, &mtb->repos_relpath, + &repos_id, &mtb->changed_rev, &mtb->changed_date, + &mtb->changed_author, &mtb->depth, &checksum, NULL, + &mtb->lock, &mtb->had_props, &properties, &update_root, + wcroot, local_relpath, scratch_pool, scratch_pool)); + + mtb->have_base = TRUE; + mtb->file_external = (update_root && mtb->kind == svn_node_file); + } /* Query the same rows in the database again for move information */ if (have_work && (mtb->have_base || mtb->have_more_work)) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - const char *cur_relpath = NULL; - int cur_op_depth; SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO_NODE)); @@ -9160,22 +9673,16 @@ read_single_info(const struct svn_wc__db_info_t **info, struct svn_wc__db_moved_to_info_t *move; int op_depth = svn_sqlite__column_int(stmt, 0); const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL); + const char *cur_relpath; move = apr_pcalloc(result_pool, sizeof(*move)); move->moved_to_abspath = svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool); - if (!cur_relpath) - { - cur_relpath = local_relpath; - cur_op_depth = relpath_depth(cur_relpath); - } - while (cur_op_depth > op_depth) - { - cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool); - cur_op_depth--; - } + cur_relpath = svn_relpath_prefix(local_relpath, op_depth, + scratch_pool); + move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath, cur_relpath, result_pool); @@ -9192,7 +9699,8 @@ read_single_info(const struct svn_wc__db_info_t **info, /* Maybe we have to get some shadowed lock from BASE to make our test suite happy... (It might be completely unrelated, but...) This queries the same BASE row again, joined to the lock table */ - if (mtb->have_base && (have_work || mtb->kind == svn_node_file)) + if (!base_tree_only && mtb->have_base + && (have_work || mtb->kind == svn_node_file)) { svn_boolean_t update_root; svn_wc__db_lock_t **lock_arg = NULL; @@ -9225,18 +9733,20 @@ read_single_info(const struct svn_wc__db_info_t **info, #ifdef HAVE_SYMLINK if (mtb->kind == svn_node_file - && (mtb->had_props || mtb->props_mod)) + && (mtb->had_props || mtb->props_mod + || (base_tree_only && properties))) { - apr_hash_t *properties; - - if (mtb->props_mod) - SVN_ERR(db_read_props(&properties, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - else - SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath, - TRUE /* deleted_ok */, - scratch_pool, scratch_pool)); + if (!base_tree_only) + { + if (mtb->props_mod) + SVN_ERR(svn_wc__db_read_props_internal(&properties, + wcroot, local_relpath, + scratch_pool, scratch_pool)); + else + SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath, + TRUE /* deleted_ok */, + scratch_pool, scratch_pool)); + } mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL)); } @@ -9246,11 +9756,17 @@ read_single_info(const struct svn_wc__db_info_t **info, mtb->copied = (original_repos_relpath != NULL); SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid, - wcroot->sdb, repos_id, result_pool)); + wcroot, repos_id, result_pool)); - if (mtb->kind == svn_node_dir) + if (!base_tree_only && mtb->kind == svn_node_dir) SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool)); + if (mtb->kind == svn_node_dir) + mtb->has_descendants = TRUE; + else + SVN_ERR(find_conflict_descendants(&mtb->has_descendants, + wcroot, local_relpath, scratch_pool)); + *info = mtb; return SVN_NO_ERROR; @@ -9260,6 +9776,7 @@ svn_error_t * svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info, svn_wc__db_t *db, const char *local_abspath, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -9274,6 +9791,7 @@ svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath, + base_tree_only, result_pool, scratch_pool), wcroot); @@ -9443,7 +9961,7 @@ svn_wc__db_read_pristine_info(svn_wc__db_status_t *status, } svn_error_t * -svn_wc__db_read_children_walker_info(apr_hash_t **nodes, +svn_wc__db_read_children_walker_info(const apr_array_header_t **items, svn_wc__db_t *db, const char *dir_abspath, apr_pool_t *result_pool, @@ -9453,6 +9971,7 @@ svn_wc__db_read_children_walker_info(apr_hash_t **nodes, const char *dir_relpath; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + apr_array_header_t *nodes; SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); @@ -9466,16 +9985,18 @@ svn_wc__db_read_children_walker_info(apr_hash_t **nodes, SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); - *nodes = apr_hash_make(result_pool); + nodes = apr_array_make(result_pool, 16, + sizeof(struct svn_wc__db_walker_info_t *)); while (have_row) { struct svn_wc__db_walker_info_t *child; const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *name = svn_relpath_basename(child_relpath, NULL); + const char *name = svn_relpath_basename(child_relpath, result_pool); int op_depth = svn_sqlite__column_int(stmt, 1); svn_error_t *err; child = apr_palloc(result_pool, sizeof(*child)); + child->name = name; child->status = svn_sqlite__column_token(stmt, 2, presence_map); if (op_depth > 0) { @@ -9484,13 +10005,16 @@ svn_wc__db_read_children_walker_info(apr_hash_t **nodes, SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); } child->kind = svn_sqlite__column_token(stmt, 3, kind_map); - svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child); + + APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child; SVN_ERR(svn_sqlite__step(&have_row, stmt)); } SVN_ERR(svn_sqlite__reset(stmt)); + *items = nodes; + return SVN_NO_ERROR; } @@ -9545,7 +10069,7 @@ svn_wc__db_read_node_install_info(const char **wcroot_abspath, if (have_row) { - if (!err && sha1_checksum) + if (sha1_checksum) err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool); if (!err && pristine_props) @@ -9574,68 +10098,48 @@ svn_wc__db_read_node_install_info(const char **wcroot_abspath, -/* The body of svn_wc__db_read_url(). +/* The body of svn_wc__db_read_repos_info(). */ static svn_error_t * -read_url_txn(const char **url, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +db_read_repos_info(svn_revnum_t *revision, + const char **repos_relpath, + apr_int64_t *repos_id, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_wc__db_status_t status; - const char *repos_relpath; - const char *repos_root_url; - apr_int64_t repos_id; - svn_boolean_t have_base; - SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL, + SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &have_base, NULL, NULL, - wcroot, local_relpath, scratch_pool, scratch_pool)); + NULL, NULL, NULL, + wcroot, local_relpath, result_pool, scratch_pool)); - if (repos_relpath == NULL) + if ((repos_relpath && !*repos_relpath) + || (repos_id && *repos_id == INVALID_REPOS_ID)) { if (status == svn_wc__db_status_added) { - SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL, + SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL, NULL, NULL, NULL, NULL, NULL, wcroot, local_relpath, - scratch_pool, scratch_pool)); + result_pool, scratch_pool)); } else if (status == svn_wc__db_status_deleted) { const char *base_del_relpath; const char *work_del_relpath; - SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL, - &work_del_relpath, - NULL, wcroot, - local_relpath, - scratch_pool, - scratch_pool)); - - if (base_del_relpath) - { - SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, - &repos_relpath, - &repos_id, - NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - wcroot, - base_del_relpath, - scratch_pool, - scratch_pool)); + SVN_ERR(scan_deletion(&base_del_relpath, NULL, + &work_del_relpath, + NULL, wcroot, + local_relpath, + scratch_pool, + scratch_pool)); - repos_relpath = svn_relpath_join( - repos_relpath, - svn_dirent_skip_ancestor(base_del_relpath, - local_relpath), - scratch_pool); - } - else + if (work_del_relpath) { /* The parent of the WORKING delete, must be an addition */ const char *work_relpath = NULL; @@ -9648,34 +10152,57 @@ read_url_txn(const char **url, work_relpath = svn_relpath_dirname(work_del_relpath, scratch_pool); - SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, + SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL, NULL, NULL, NULL, NULL, NULL, wcroot, work_relpath, scratch_pool, scratch_pool)); - repos_relpath = svn_relpath_join( - repos_relpath, + if (repos_relpath) + *repos_relpath = svn_relpath_join( + *repos_relpath, svn_dirent_skip_ancestor(work_relpath, local_relpath), - scratch_pool); + result_pool); + } + else + { + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision, + repos_relpath, + repos_id, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + wcroot, + base_del_relpath, + scratch_pool, + scratch_pool)); + + if (repos_relpath) + *repos_relpath = svn_relpath_join( + *repos_relpath, + svn_dirent_skip_ancestor(base_del_relpath, + local_relpath), + result_pool); } } else if (status == svn_wc__db_status_excluded) { const char *parent_relpath; const char *name; - const char *url2; - /* Set 'url' to the *full URL* of the parent WC dir, - * and 'name' to the *single path component* that is the - * basename of this WC directory, so that joining them will result - * in the correct full URL. */ + /* A BASE excluded would have had repository information, so + we have a working exclude, which must be below an addition */ + svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool); - SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL, + NULL, NULL, NULL, NULL, NULL, + wcroot, parent_relpath, + scratch_pool, scratch_pool)); - *url = svn_path_url_add_component2(url2, name, result_pool); + if (repos_relpath) + *repos_relpath = svn_relpath_join(*repos_relpath, name, + result_pool); return SVN_NO_ERROR; } @@ -9687,26 +10214,23 @@ read_url_txn(const char **url, } } - SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb, - repos_id, scratch_pool)); - - SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL); - *url = svn_path_url_add_component2(repos_root_url, repos_relpath, - result_pool); - return SVN_NO_ERROR; } svn_error_t * -svn_wc__db_read_url(const char **url, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +svn_wc__db_read_repos_info(svn_revnum_t *revision, + const char **repos_relpath, + const char **repos_root_url, + const char **repos_uuid, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; + apr_int64_t repos_id = INVALID_REPOS_ID; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); @@ -9715,9 +10239,17 @@ svn_wc__db_read_url(const char **url, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath, - result_pool, scratch_pool), - wcroot); + SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath, + (repos_root_url || repos_uuid) + ? &repos_id : NULL, + wcroot, local_relpath, + result_pool, scratch_pool), + svn_wc__db_fetch_repos_info(repos_root_url, + repos_uuid, + wcroot, repos_id, + result_pool), + SVN_NO_ERROR, SVN_NO_ERROR, + wcroot); return SVN_NO_ERROR; } @@ -9861,12 +10393,12 @@ svn_wc__db_read_props_streamily(svn_wc__db_t *db, /* Helper for svn_wc__db_read_props(). */ -static svn_error_t * -db_read_props(apr_hash_t **props, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +svn_error_t * +svn_wc__db_read_props_internal(apr_hash_t **props, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; @@ -9923,8 +10455,10 @@ svn_wc__db_read_props(apr_hash_t **props, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath, - result_pool, scratch_pool), + SVN_WC__DB_WITH_TXN(svn_wc__db_read_props_internal(props, wcroot, + local_relpath, + result_pool, + scratch_pool), wcroot); return SVN_NO_ERROR; @@ -10155,7 +10689,7 @@ filter_unwanted_props(apr_hash_t *prop_hash, hi; hi = apr_hash_next(hi)) { - const char *ipropname = svn__apr_hash_index_key(hi); + const char *ipropname = apr_hash_this_key(hi); if (strcmp(ipropname, propname) != 0) svn_hash_sets(prop_hash, ipropname, NULL); @@ -10329,7 +10863,7 @@ db_read_inherited_props(apr_array_header_t **inherited_props, iprop_elt->prop_hash = node_props; /* Build the output array in depth-first order. */ - svn_sort__array_insert(&iprop_elt, iprops, 0); + svn_sort__array_insert(iprops, &iprop_elt, 0); } } } @@ -10365,7 +10899,7 @@ db_read_inherited_props(apr_array_header_t **inherited_props, /* If we didn't filter everything then keep this iprop. */ if (apr_hash_count(cached_iprop->prop_hash)) - svn_sort__array_insert(&cached_iprop, iprops, 0); + svn_sort__array_insert(iprops, &cached_iprop, 0); } } @@ -10479,7 +11013,7 @@ get_children_with_cached_iprops(apr_hash_t **iprop_paths, hi; hi = apr_hash_next(hi)) { - const char *child_abspath = svn__apr_hash_index_key(hi); + const char *child_abspath = apr_hash_this_key(hi); const char *child_relpath; svn_node_kind_t child_kind; @@ -10557,8 +11091,34 @@ svn_wc__db_read_children_of_working_node(const apr_array_header_t **children, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return gather_children2(children, wcroot, local_relpath, - result_pool, scratch_pool); + return svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_WORKING_CHILDREN, -1, + result_pool, scratch_pool)); +} + +svn_error_t * +svn_wc__db_base_read_not_present_children( + const apr_array_header_t **children, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + local_abspath, + scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + return svn_error_trace( + gather_children(children, wcroot, local_relpath, + STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1, + result_pool, scratch_pool)); } /* Helper for svn_wc__db_node_check_replace(). @@ -10745,75 +11305,27 @@ svn_wc__db_read_children(const apr_array_header_t **children, VERIFY_USABLE_WCROOT(wcroot); return gather_children(children, wcroot, local_relpath, + STMT_SELECT_NODE_CHILDREN, -1, result_pool, scratch_pool); } -/* */ +/* Implementation of svn_wc__db_global_relocate */ static svn_error_t * relocate_txn(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, const char *repos_root_url, - const char *repos_uuid, - svn_boolean_t have_base_node, - apr_int64_t old_repos_id, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; apr_int64_t new_repos_id; - - /* This function affects all the children of the given local_relpath, - but the way that it does this is through the repos inheritance mechanism. - So, we only need to rewrite the repos_id of the given local_relpath, - as well as any children with a non-null repos_id, as well as various - repos_id fields in the locks and working_node tables. - */ - - /* Get the repos_id for the new repository. */ - SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid, - wcroot->sdb, scratch_pool)); - - /* Set the (base and working) repos_ids and clear the dav_caches */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_RECURSIVE_UPDATE_NODE_REPO)); - SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath, - old_repos_id, new_repos_id)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - if (have_base_node) - { - /* Update any locks for the root or its children. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_UPDATE_LOCK_REPOS_ID)); - SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id)); - SVN_ERR(svn_sqlite__step_done(stmt)); - } - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_wc__db_global_relocate(svn_wc__db_t *db, - const char *local_dir_abspath, - const char *repos_root_url, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; const char *local_dir_relpath; svn_wc__db_status_t status; const char *repos_uuid; svn_boolean_t have_base_node; apr_int64_t old_repos_id; - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath)); - /* ### assert that we were passed a directory? */ - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath, - db, local_dir_abspath, scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - local_relpath = local_dir_relpath; + local_dir_relpath = local_relpath; SVN_ERR(read_info(&status, NULL, NULL, NULL, &old_repos_id, @@ -10852,11 +11364,11 @@ svn_wc__db_global_relocate(svn_wc__db_t *db, { const char *work_del_relpath; - SVN_ERR(scan_deletion_txn(NULL, NULL, - &work_del_relpath, NULL, - wcroot, local_dir_relpath, - scratch_pool, - scratch_pool)); + SVN_ERR(scan_deletion(NULL, NULL, + &work_del_relpath, NULL, + wcroot, local_dir_relpath, + scratch_pool, + scratch_pool)); if (work_del_relpath) { /* Deleted within a copy/move */ @@ -10884,15 +11396,62 @@ svn_wc__db_global_relocate(svn_wc__db_t *db, scratch_pool, scratch_pool)); } - SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb, + SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot, old_repos_id, scratch_pool)); - SVN_ERR_ASSERT(repos_uuid); + SVN_ERR_ASSERT(repos_uuid); /* This function affects all the children of the given local_relpath, + but the way that it does this is through the repos inheritance mechanism. + So, we only need to rewrite the repos_id of the given local_relpath, + as well as any children with a non-null repos_id, as well as various + repos_id fields in the locks and working_node tables. + */ + + /* Get the repos_id for the new repository. */ + SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid, + wcroot->sdb, scratch_pool)); + + /* Set the (base and working) repos_ids and clear the dav_caches */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_RECURSIVE_UPDATE_NODE_REPO)); + SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath, + old_repos_id, new_repos_id)); + SVN_ERR(svn_sqlite__step_done(stmt)); + + if (have_base_node) + { + /* Update any locks for the root or its children. */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_LOCK_REPOS_ID)); + SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_wc__db_global_relocate(svn_wc__db_t *db, + const char *local_dir_abspath, + const char *repos_root_url, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath)); + /* ### assert that we were passed a directory? */ + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, + db, local_dir_abspath, scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid, - have_base_node, old_repos_id, scratch_pool), + relocate_txn(wcroot, local_relpath, repos_root_url, scratch_pool), wcroot); + SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_infinity, + scratch_pool)); + return SVN_NO_ERROR; } @@ -10979,6 +11538,48 @@ determine_commit_repos_info(apr_int64_t *repos_id, return svn_error_trace(svn_sqlite__reset(stmt)); } +static svn_error_t * +moved_descendant_collect(apr_hash_t **map, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + + *map = NULL; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, + op_depth)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (! have_row) + return svn_error_trace(svn_sqlite__reset(stmt)); + + /* Find all moved descendants. Key them on target, because that is + always unique */ + while (have_row) + { + const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool); + const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool); + + if (!*map) + *map = apr_hash_make(result_pool); + + svn_hash_sets(*map, to_relpath, src_relpath); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + SVN_ERR(svn_sqlite__reset(stmt)); + + return SVN_NO_ERROR; +} + /* Helper for svn_wc__db_global_commit() Makes local_relpath and all its descendants at the same op-depth represent @@ -10990,49 +11591,25 @@ determine_commit_repos_info(apr_int64_t *repos_id, Assumptions: * local_relpath is not the working copy root (can't be moved) * repos_relpath is not the repository root (can't be moved) - */ + */ static svn_error_t * moved_descendant_commit(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - int op_depth, apr_int64_t repos_id, const char *repos_relpath, svn_revnum_t revision, + apr_hash_t *children, apr_pool_t *scratch_pool) { - apr_hash_t *children; apr_pool_t *iterpool; svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; apr_hash_index_t *hi; SVN_ERR_ASSERT(*local_relpath != '\0' && *repos_relpath != '\0'); - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_MOVED_DESCENDANTS)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - local_relpath, - op_depth)); - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (! have_row) - return svn_error_trace(svn_sqlite__reset(stmt)); - - children = apr_hash_make(scratch_pool); - - /* First, obtain all moved children */ - /* To keep error handling simple, first cache them in a hashtable */ - while (have_row) - { - const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); - const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); - - svn_hash_sets(children, src_relpath, to_relpath); - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - SVN_ERR(svn_sqlite__reset(stmt)); + if (!children) + return SVN_NO_ERROR; /* Then update them */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -11041,11 +11618,12 @@ moved_descendant_commit(svn_wc__db_wcroot_t *wcroot, iterpool = svn_pool_create(scratch_pool); for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi)) { - const char *src_relpath = svn__apr_hash_index_key(hi); - const char *to_relpath = svn__apr_hash_index_val(hi); + const char *src_relpath = apr_hash_this_val(hi); + const char *to_relpath = apr_hash_this_key(hi); const char *new_repos_relpath; int to_op_depth = relpath_depth(to_relpath); int affected; + apr_hash_t *map; svn_pool_clear(iterpool); @@ -11072,9 +11650,11 @@ moved_descendant_commit(svn_wc__db_wcroot_t *wcroot, SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */ #endif - SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth, + SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth, + iterpool, iterpool)); + SVN_ERR(moved_descendant_commit(wcroot, to_relpath, repos_id, new_repos_relpath, revision, - iterpool)); + map, iterpool)); } svn_pool_destroy(iterpool); @@ -11133,7 +11713,6 @@ commit_node(svn_wc__db_wcroot_t *wcroot, apr_time_t changed_date, const char *changed_author, const svn_checksum_t *new_checksum, - const apr_array_header_t *new_children, apr_hash_t *new_dav_cache, svn_boolean_t keep_changelist, svn_boolean_t no_unlock, @@ -11155,6 +11734,7 @@ commit_node(svn_wc__db_wcroot_t *wcroot, const char *repos_relpath; int op_depth; svn_wc__db_status_t old_presence; + svn_boolean_t moved_here; /* If we are adding a file or directory, then we need to get repository information from the parent node since "this node" does @@ -11184,6 +11764,7 @@ commit_node(svn_wc__db_wcroot_t *wcroot, /* Figure out the new node's kind. It will be whatever is in WORKING_NODE, or there will be a BASE_NODE that has it. */ + old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map); new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map); /* What will the new depth be? */ @@ -11202,26 +11783,35 @@ commit_node(svn_wc__db_wcroot_t *wcroot, svn_sqlite__column_text(stmt_info, 2, NULL)) == 0); } - /* Find the appropriate new properties -- ACTUAL overrides any properties - in WORKING that arrived as part of a copy/move. + if (old_presence != svn_wc__db_status_base_deleted) + { + /* Find the appropriate new properties -- ACTUAL overrides any properties + in WORKING that arrived as part of a copy/move. - Note: we'll keep them as a big blob of data, rather than - deserialize/serialize them. */ - if (have_act) - prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len, - scratch_pool); - if (prop_blob.data == NULL) - prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len, - scratch_pool); - - inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16, - &inherited_prop_blob.len, - scratch_pool); + Note: we'll keep them as a big blob of data, rather than + deserialize/serialize them. */ + if (have_act) + prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len, + scratch_pool); + if (prop_blob.data == NULL) + prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len, + scratch_pool); - if (keep_changelist && have_act) - changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool); + inherited_prop_blob.data = svn_sqlite__column_blob( + stmt_info, 16, + &inherited_prop_blob.len, + scratch_pool); - old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map); + if (keep_changelist && have_act) + changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool); + + moved_here = svn_sqlite__column_int(stmt_info, 15); + } + else + { + moved_here = FALSE; + changelist = NULL; + } /* ### other stuff? */ @@ -11232,6 +11822,24 @@ commit_node(svn_wc__db_wcroot_t *wcroot, { int affected_rows; + SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath)); + + /* First clear the moves that we are going to delete in a bit */ + { + apr_hash_t *old_moves; + apr_hash_index_t *hi; + SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0, + scratch_pool, scratch_pool)); + + if (old_moves) + for (hi = apr_hash_first(scratch_pool, old_moves); + hi; hi = apr_hash_next(hi)) + { + SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi), + scratch_pool)); + } + } + /* This removes all layers of this node and at the same time determines if we need to remove shadowed layers below our descendants. */ @@ -11264,26 +11872,40 @@ commit_node(svn_wc__db_wcroot_t *wcroot, be integrated, they really affect a different op-depth and completely different nodes (via a different recursion pattern). */ - /* Collapse descendants of the current op_depth in layer 0 */ - SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth, - repos_id, repos_relpath, new_revision, - scratch_pool)); + if (old_presence != svn_wc__db_status_base_deleted) + { + /* Collapse descendants of the current op_depth to layer 0, + this includes moved-from/to clearing */ + SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth, + repos_id, repos_relpath, new_revision, + scratch_pool)); + } + + if (old_presence != svn_wc__db_status_base_deleted) + { + apr_hash_t *moves = NULL; + + SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0, + scratch_pool, scratch_pool)); - /* And make the recorded local moves represent moves of the node we just - committed. */ - SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0, + /* And make the recorded local moves represent moves of the node we + just committed. */ + SVN_ERR(moved_descendant_commit(wcroot, local_relpath, repos_id, repos_relpath, new_revision, - scratch_pool)); + moves, scratch_pool)); + } - /* This node is no longer modified, so no node was moved here */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_CLEAR_MOVED_TO_FROM_DEST)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, - local_relpath)); + if (moved_here) + { + /* This node is no longer modified, so no node was moved here */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_CLEAR_MOVED_TO_FROM_DEST)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, + local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__step_done(stmt)); + } } - /* Update or add the BASE_NODE row with all the new information. */ if (*local_relpath == '\0') @@ -11292,38 +11914,56 @@ commit_node(svn_wc__db_wcroot_t *wcroot, parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool); /* Preserve any incomplete status */ - new_presence = (old_presence == svn_wc__db_status_incomplete - ? svn_wc__db_status_incomplete - : svn_wc__db_status_normal); - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_APPLY_CHANGES_TO_BASE_NODE)); - /* symlink_target not yet used */ - SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn", - wcroot->wc_id, local_relpath, - parent_relpath, - repos_id, - repos_relpath, - new_revision, - presence_map, new_presence, - new_depth_str, - kind_map, new_kind, - changed_rev, - changed_date, - changed_author, - prop_blob.data, prop_blob.len)); - - SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum, - scratch_pool)); - SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache, - scratch_pool)); - if (inherited_prop_blob.data != NULL) + if (old_presence != svn_wc__db_status_base_deleted) { - SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data, - inherited_prop_blob.len)); + new_presence = (old_presence == svn_wc__db_status_incomplete + ? svn_wc__db_status_incomplete + : svn_wc__db_status_normal); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_APPLY_CHANGES_TO_BASE_NODE)); + /* symlink_target not yet used */ + SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn", + wcroot->wc_id, local_relpath, + parent_relpath, + repos_id, + repos_relpath, + new_revision, + presence_map, new_presence, + new_depth_str, + kind_map, new_kind, + changed_rev, + changed_date, + changed_author, + prop_blob.data, prop_blob.len)); + + SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum, + scratch_pool)); + SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache, + scratch_pool)); + if (inherited_prop_blob.data != NULL) + { + SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data, + inherited_prop_blob.len)); + } + + SVN_ERR(svn_sqlite__step_done(stmt)); } + else + { + struct insert_base_baton_t ibb; + blank_ibb(&ibb); - SVN_ERR(svn_sqlite__step_done(stmt)); + ibb.repos_id = repos_id; + ibb.status = svn_wc__db_status_not_present; + ibb.kind = new_kind; + ibb.repos_relpath = repos_relpath; + ibb.revision = new_revision; + + SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool)); + + keep_changelist = FALSE; /* Nothing there */ + } if (have_act) { @@ -11351,23 +11991,21 @@ commit_node(svn_wc__db_wcroot_t *wcroot, } } - if (new_kind == svn_node_dir) - { - /* When committing a directory, we should have its new children. */ - /* ### one day. just not today. */ -#if 0 - SVN_ERR_ASSERT(new_children != NULL); -#endif - - /* ### process the children */ - } - if (!no_unlock) { svn_sqlite__stmt_t *lock_stmt; + svn_boolean_t op_root = (op_depth > 0 + && (relpath_depth(local_relpath) == op_depth)); + /* If we are committing an add of a delete, we can assume we own + all locks at or below REPOS_RELPATH (or the server would have + denied the commit). As we must have passed these to the server + we can now safely remove them. + */ SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb, - STMT_DELETE_LOCK_RECURSIVELY)); + op_root + ? STMT_DELETE_LOCK_RECURSIVELY + : STMT_DELETE_LOCK)); SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath)); SVN_ERR(svn_sqlite__step_done(lock_stmt)); } @@ -11387,7 +12025,6 @@ svn_wc__db_global_commit(svn_wc__db_t *db, apr_time_t changed_date, const char *changed_author, const svn_checksum_t *new_checksum, - const apr_array_header_t *new_children, apr_hash_t *new_dav_cache, svn_boolean_t keep_changelist, svn_boolean_t no_unlock, @@ -11399,7 +12036,6 @@ svn_wc__db_global_commit(svn_wc__db_t *db, SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision)); - SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL); SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, local_abspath, scratch_pool, scratch_pool)); @@ -11408,7 +12044,7 @@ svn_wc__db_global_commit(svn_wc__db_t *db, SVN_WC__DB_WITH_TXN( commit_node(wcroot, local_relpath, new_revision, changed_revision, changed_date, changed_author, - new_checksum, new_children, new_dav_cache, keep_changelist, + new_checksum, new_dav_cache, keep_changelist, no_unlock, work_items, scratch_pool), wcroot); @@ -11543,6 +12179,9 @@ db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot, * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH, * NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision. * + * NODE_STATUS, NODE_KIND, NODE_REVISION and NODE_REPOS_RELPATH represent the + * values as stored currently in WCROOT for LOCAL_RELPATH. + * * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute * working copy paths to depth-first ordered arrays of * svn_prop_inherited_item_t * structures. If the absolute path equivalent @@ -11555,6 +12194,10 @@ db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot, static svn_error_t * bump_node_revision(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + svn_wc__db_status_t node_status, + svn_node_kind_t node_kind, + svn_revnum_t node_revision, + const char *node_repos_relpath, apr_int64_t new_repos_id, const char *new_repos_relpath, svn_revnum_t new_rev, @@ -11567,56 +12210,14 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool) { apr_pool_t *iterpool; - const apr_array_header_t *children; - int i; - svn_wc__db_status_t status; - svn_node_kind_t db_kind; - svn_revnum_t revision; - const char *repos_relpath; - apr_int64_t repos_id; + apr_hash_t *children; + apr_hash_index_t *hi; svn_boolean_t set_repos_relpath = FALSE; - svn_boolean_t update_root; svn_depth_t depth_below_here = depth; apr_array_header_t *iprops = NULL; - /* Skip an excluded path and its descendants. */ - if (svn_hash_gets(exclude_relpaths, local_relpath)) - return SVN_NO_ERROR; - - SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision, - &repos_relpath, &repos_id, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, &update_root, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - - /* Skip file externals */ - if (update_root - && db_kind == svn_node_file - && !is_root) - return SVN_NO_ERROR; - - if (skip_when_dir && db_kind == svn_node_dir) - return SVN_NO_ERROR; - - /* If the node is still marked 'not-present', then the server did not - re-add it. So it's really gone in this revision, thus we remove the node. - - If the node is still marked 'server-excluded' and yet is not the same - revision as new_rev, then the server did not re-add it, nor - re-server-exclude it, so we can remove the node. */ - if (!is_root - && (status == svn_wc__db_status_not_present - || (status == svn_wc__db_status_server_excluded && - revision != new_rev))) - { - return svn_error_trace(db_base_remove(wcroot, local_relpath, - db, FALSE, FALSE, FALSE, - SVN_INVALID_REVNUM, - NULL, NULL, scratch_pool)); - } - - if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath)) + if (new_repos_relpath != NULL + && strcmp(node_repos_relpath, new_repos_relpath)) set_repos_relpath = TRUE; if (wcroot_iprops) @@ -11626,7 +12227,7 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, if (iprops || set_repos_relpath - || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision)) + || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != node_revision)) { SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath, iprops, new_rev, @@ -11638,10 +12239,10 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, /* Early out */ if (depth <= svn_depth_empty - || db_kind != svn_node_dir - || status == svn_wc__db_status_server_excluded - || status == svn_wc__db_status_excluded - || status == svn_wc__db_status_not_present) + || node_kind != svn_node_dir + || node_status == svn_wc__db_status_server_excluded + || node_status == svn_wc__db_status_excluded + || node_status == svn_wc__db_status_not_present) return SVN_NO_ERROR; /* And now recurse over the children */ @@ -11653,25 +12254,62 @@ bump_node_revision(svn_wc__db_wcroot_t *wcroot, iterpool = svn_pool_create(scratch_pool); - SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0, - scratch_pool, iterpool)); - for (i = 0; i < children->nelts; i++) + SVN_ERR(base_get_children_info(&children, wcroot, local_relpath, 0, + scratch_pool, iterpool)); + for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi)) { - const char *child_basename = APR_ARRAY_IDX(children, i, const char *); + const char *child_basename = apr_hash_this_key(hi); + const struct svn_wc__db_base_info_t *child_info; const char *child_local_relpath; const char *child_repos_relpath = NULL; svn_pool_clear(iterpool); + child_info = apr_hash_this_val(hi); + + if (child_info->update_root && child_info->kind == svn_node_file) + continue; /* Skip file externals */ + + if (depth < svn_depth_immediates && child_info->kind == svn_node_dir) + continue; /* Skip directories */ + + child_local_relpath = svn_relpath_join(local_relpath, child_basename, + iterpool); + + /* Don't touch nodes that can't be touched via the exclude list */ + if (svn_hash_gets(exclude_relpaths, child_local_relpath)) + continue; + + /* If the node is still marked 'not-present', then the server did not + re-add it. So it's really gone in this revision, thus we remove the + node. + + If the node is still marked 'server-excluded' and yet is not the same + revision as new_rev, then the server did not re-add it, nor + re-server-exclude it, so we can remove the node. */ + if (child_info->status == svn_wc__db_status_not_present + || (child_info->status == svn_wc__db_status_server_excluded && + child_info->revnum != new_rev)) + { + svn_sqlite__stmt_t *stmt; + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_BASE_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, child_local_relpath)); + SVN_ERR(svn_sqlite__step_done(stmt)); + continue; + } + /* Derive the new URL for the current (child) entry */ if (new_repos_relpath) child_repos_relpath = svn_relpath_join(new_repos_relpath, child_basename, iterpool); - child_local_relpath = svn_relpath_join(local_relpath, child_basename, - iterpool); - - SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id, + SVN_ERR(bump_node_revision(wcroot, child_local_relpath, + child_info->status, + child_info->kind, + child_info->revnum, + child_info->repos_relpath, + new_repos_id, child_repos_relpath, new_rev, depth_below_here, exclude_relpaths, wcroot_iprops, @@ -11699,6 +12337,7 @@ bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot, svn_revnum_t new_revision, apr_hash_t *exclude_relpaths, apr_hash_t *wcroot_iprops, + svn_boolean_t empty_update, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) @@ -11707,8 +12346,11 @@ bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot, svn_node_kind_t kind; svn_error_t *err; apr_int64_t new_repos_id = INVALID_REPOS_ID; + svn_revnum_t revision; + const char *repos_relpath; - err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL, + err = svn_wc__db_base_get_info_internal(&status, &kind, &revision, + &repos_relpath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, wcroot, local_relpath, @@ -11738,13 +12380,16 @@ bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot, new_repos_uuid, wcroot->sdb, scratch_pool)); - SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id, + SVN_ERR(bump_node_revision(wcroot, local_relpath, + status, kind, revision, repos_relpath, + new_repos_id, new_repos_relpath, new_revision, depth, exclude_relpaths, wcroot_iprops, TRUE /* is_root */, FALSE, db, scratch_pool)); + /* ### TODO: Use empty_update flag for change knowledge */ SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db, scratch_pool)); @@ -11765,6 +12410,7 @@ svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db, svn_revnum_t new_revision, apr_hash_t *exclude_relpaths, apr_hash_t *wcroot_iprops, + svn_boolean_t empty_update, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool) @@ -11787,7 +12433,7 @@ svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db, bump_revisions_post_update(wcroot, local_relpath, db, depth, new_repos_relpath, new_repos_root_url, new_repos_uuid, new_revision, - exclude_relpaths, wcroot_iprops, + exclude_relpaths, wcroot_iprops, empty_update, notify_func, notify_baton, scratch_pool), wcroot); @@ -11864,6 +12510,7 @@ svn_wc__db_lock_add(svn_wc__db_t *db, static svn_error_t * lock_remove_txn(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + svn_skel_t *work_items, apr_pool_t *scratch_pool) { const char *repos_relpath; @@ -11883,6 +12530,8 @@ lock_remove_txn(svn_wc__db_wcroot_t *wcroot, SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); + return SVN_NO_ERROR; } @@ -11890,6 +12539,7 @@ lock_remove_txn(svn_wc__db_wcroot_t *wcroot, svn_error_t * svn_wc__db_lock_remove(svn_wc__db_t *db, const char *local_abspath, + svn_skel_t *work_items, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; @@ -11902,7 +12552,7 @@ svn_wc__db_lock_remove(svn_wc__db_t *db, VERIFY_USABLE_WCROOT(wcroot); SVN_WC__DB_WITH_TXN( - lock_remove_txn(wcroot, local_relpath, scratch_pool), + lock_remove_txn(wcroot, local_relpath, work_items, scratch_pool), wcroot); /* There may be some entries, and the lock info is now out of date. */ @@ -11911,39 +12561,6 @@ svn_wc__db_lock_remove(svn_wc__db_t *db, return SVN_NO_ERROR; } - -svn_error_t * -svn_wc__db_scan_base_repos(const char **repos_relpath, - const char **repos_root_url, - const char **repos_uuid, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - apr_int64_t repos_id; - - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, - local_abspath, scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - - SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, - repos_relpath, &repos_id, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - wcroot, local_relpath, - result_pool, scratch_pool)); - SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb, - repos_id, result_pool)); - - return SVN_NO_ERROR; -} - - /* A helper for scan_addition(). * Compute moved-from information for the node at LOCAL_RELPATH which * has been determined as having been moved-here. @@ -12042,23 +12659,26 @@ get_moved_from_info(const char **moved_from_relpath, return SVN_NO_ERROR; } -/* The body of scan_addition(). - */ +/* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of + DB+LOCAL_ABSPATH. + + The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there + is no 'copy-from' repository. */ static svn_error_t * -scan_addition_txn(svn_wc__db_status_t *status, - const char **op_root_relpath_p, - const char **repos_relpath, - apr_int64_t *repos_id, - const char **original_repos_relpath, - apr_int64_t *original_repos_id, - svn_revnum_t *original_revision, - const char **moved_from_relpath, - const char **moved_from_op_root_relpath, - int *moved_from_op_depth, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +scan_addition(svn_wc__db_status_t *status, + const char **op_root_relpath_p, + const char **repos_relpath, + apr_int64_t *repos_id, + const char **original_repos_relpath, + apr_int64_t *original_repos_id, + svn_revnum_t *original_revision, + const char **moved_from_relpath, + const char **moved_from_op_root_relpath, + int *moved_from_op_depth, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const char *op_root_relpath; const char *build_relpath = ""; @@ -12087,8 +12707,7 @@ scan_addition_txn(svn_wc__db_status_t *status, svn_boolean_t have_row; svn_wc__db_status_t presence; int op_depth; - const char *repos_prefix_path = ""; - int i; + const char *repos_prefix_path; /* ### is it faster to fetch fewer columns? */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -12137,17 +12756,10 @@ scan_addition_txn(svn_wc__db_status_t *status, /* Calculate the op root local path components */ - op_root_relpath = local_relpath; - - for (i = relpath_depth(local_relpath); i > op_depth; --i) - { - /* Calculate the path of the operation root */ - repos_prefix_path = - svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL), - repos_prefix_path, - scratch_pool); - op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool); - } + op_root_relpath = svn_relpath_prefix(local_relpath, op_depth, + scratch_pool); + repos_prefix_path = svn_relpath_skip_ancestor(op_root_relpath, + local_relpath); if (op_root_relpath_p) *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath); @@ -12239,39 +12851,37 @@ scan_addition_txn(svn_wc__db_status_t *status, { const char *base_relpath; - while (TRUE) - { + while (TRUE) + { + const char *tmp; - SVN_ERR(svn_sqlite__reset(stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); - /* Pointing at op_depth, look at the parent */ - repos_prefix_path = - svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL), - repos_prefix_path, - scratch_pool); - op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool); + /* Pointing at op_depth, look at the parent */ + repos_prefix_path = + svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL), + repos_prefix_path, + scratch_pool); + op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (! have_row) - break; + if (! have_row) + break; - op_depth = svn_sqlite__column_int(stmt, 0); + op_depth = svn_sqlite__column_int(stmt, 0); - /* Skip to op_depth */ - for (i = relpath_depth(op_root_relpath); i > op_depth; i--) - { - /* Calculate the path of the operation root */ - repos_prefix_path = - svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL), - repos_prefix_path, - scratch_pool); - op_root_relpath = - svn_relpath_dirname(op_root_relpath, scratch_pool); + /* Skip to op_depth */ + tmp = op_root_relpath; + + op_root_relpath = svn_relpath_prefix(op_root_relpath, op_depth, + scratch_pool); + repos_prefix_path = svn_relpath_join( + svn_relpath_skip_ancestor(op_root_relpath, tmp), + repos_prefix_path, scratch_pool); } - } SVN_ERR(svn_sqlite__reset(stmt)); @@ -12331,39 +12941,27 @@ scan_addition_txn(svn_wc__db_status_t *status, return SVN_NO_ERROR; } - -/* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of - DB+LOCAL_ABSPATH. - - The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there - is no 'copy-from' repository. */ -static svn_error_t * -scan_addition(svn_wc__db_status_t *status, - const char **op_root_relpath, +svn_error_t * +svn_wc__db_scan_addition_internal( + svn_wc__db_status_t *status, + const char **op_root_relpath_p, const char **repos_relpath, apr_int64_t *repos_id, const char **original_repos_relpath, apr_int64_t *original_repos_id, svn_revnum_t *original_revision, - const char **moved_from_relpath, - const char **moved_from_op_root_relpath, - int *moved_from_op_depth, svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - SVN_WC__DB_WITH_TXN( - scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id, - original_repos_relpath, original_repos_id, - original_revision, moved_from_relpath, - moved_from_op_root_relpath, moved_from_op_depth, - wcroot, local_relpath, result_pool, scratch_pool), - wcroot); - return SVN_NO_ERROR; + return svn_error_trace( + scan_addition(status, op_root_relpath_p, repos_relpath, repos_id, + original_repos_relpath, original_repos_id, + original_revision, NULL, NULL, NULL, + wcroot, local_relpath, result_pool, scratch_pool)); } - svn_error_t * svn_wc__db_scan_addition(svn_wc__db_status_t *status, const char **op_root_abspath, @@ -12395,7 +12993,8 @@ svn_wc__db_scan_addition(svn_wc__db_status_t *status, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(scan_addition(status, + SVN_WC__DB_WITH_TXN4( + scan_addition(status, op_root_abspath ? &op_root_relpath : NULL, @@ -12403,7 +13002,14 @@ svn_wc__db_scan_addition(svn_wc__db_status_t *status, original_repos_relpath, original_repos_id_p, original_revision, NULL, NULL, NULL, - wcroot, local_relpath, result_pool, scratch_pool)); + wcroot, local_relpath, result_pool, scratch_pool), + svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot, + repos_id, result_pool), + svn_wc__db_fetch_repos_info(original_root_url, original_uuid, + wcroot, original_repos_id, + result_pool), + SVN_NO_ERROR, + wcroot); if (op_root_abspath) *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath, @@ -12411,12 +13017,6 @@ svn_wc__db_scan_addition(svn_wc__db_status_t *status, /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */ SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID); - SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb, - repos_id, result_pool)); - SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid, - wcroot->sdb, original_repos_id, - result_pool)); - return SVN_NO_ERROR; } @@ -12444,7 +13044,8 @@ svn_wc__db_scan_moved(const char **moved_from_abspath, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(scan_addition(&status, + SVN_WC__DB_WITH_TXN( + scan_addition(&status, op_root_abspath ? &op_root_relpath : NULL, @@ -12460,7 +13061,8 @@ svn_wc__db_scan_moved(const char **moved_from_abspath, moved_from_delete_abspath ? &moved_from_op_depth : NULL, - wcroot, local_relpath, scratch_pool, scratch_pool)); + wcroot, local_relpath, scratch_pool, scratch_pool), + wcroot); if (status != svn_wc__db_status_moved_here || !moved_from_relpath) return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, @@ -12484,12 +13086,8 @@ svn_wc__db_scan_moved(const char **moved_from_abspath, /* The deleted node is either where we moved from, or one of its ancestors */ if (moved_from_delete_abspath) { - const char *tmp = moved_from_op_root_relpath; - - SVN_ERR_ASSERT(moved_from_op_depth >= 0); - - while (relpath_depth(tmp) > moved_from_op_depth) - tmp = svn_relpath_dirname(tmp, scratch_pool); + const char *tmp = svn_relpath_prefix(moved_from_op_root_relpath, + moved_from_op_depth, scratch_pool); *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp, scratch_pool); @@ -12498,26 +13096,25 @@ svn_wc__db_scan_moved(const char **moved_from_abspath, return SVN_NO_ERROR; } -/* ### +/* ### Recursive helper for svn_wc__db_follow_moved_to() */ static svn_error_t * -follow_moved_to(apr_array_header_t **moved_tos, - int op_depth, - const char *repos_path, - svn_revnum_t revision, - svn_wc__db_wcroot_t *wcroot, +follow_moved_to(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + int op_depth, + apr_array_header_t **moved_tos, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - int working_op_depth; - const char *ancestor_relpath, *node_moved_to = NULL; + int shadowing_op_depth; + const char *ancestor_relpath; + const char *node_moved_to = NULL; int i; - SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path)); - + /* Obtain the depth of the node directly shadowing local_relpath + as it exists at OP_DEPTH, and perhaps moved to info */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_OP_DEPTH_MOVED_TO)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, @@ -12525,57 +13122,35 @@ follow_moved_to(apr_array_header_t **moved_tos, SVN_ERR(svn_sqlite__step(&have_row, stmt)); if (have_row) { - working_op_depth = svn_sqlite__column_int(stmt, 0); + shadowing_op_depth = svn_sqlite__column_int(stmt, 0); node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool); - if (!repos_path) + + if (node_moved_to) { - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (!have_row || svn_sqlite__column_revnum(stmt, 0)) - return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, - svn_sqlite__reset(stmt), - _("The base node '%s' was not found."), - path_for_error_message(wcroot, - local_relpath, - scratch_pool)); - repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool); - revision = svn_sqlite__column_revnum(stmt, 3); + struct svn_wc__db_moved_to_t *moved_to; + + moved_to = apr_palloc(result_pool, sizeof(*moved_to)); + moved_to->op_depth = shadowing_op_depth; + moved_to->local_relpath = node_moved_to; + APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to; } } + SVN_ERR(svn_sqlite__reset(stmt)); - if (node_moved_to) + if (!have_row) { - svn_boolean_t have_row2; - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_MOVED_HERE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to, - relpath_depth(node_moved_to))); - SVN_ERR(svn_sqlite__step(&have_row2, stmt)); - if (!have_row2 || !svn_sqlite__column_int(stmt, 0) - || revision != svn_sqlite__column_revnum(stmt, 3) - || strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL))) - node_moved_to = NULL; - SVN_ERR(svn_sqlite__reset(stmt)); + /* Node is not shadowed, so not moved */ + return SVN_NO_ERROR; } - - if (node_moved_to) + else if (node_moved_to) { - struct svn_wc__db_moved_to_t *moved_to; - - moved_to = apr_palloc(result_pool, sizeof(*moved_to)); - moved_to->op_depth = working_op_depth; - moved_to->local_relpath = node_moved_to; - APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to; + /* Moved directly, so we have the final location */ + return SVN_NO_ERROR; } - - /* A working row with moved_to, or no working row, and we are done. */ - if (node_moved_to || !have_row) - return SVN_NO_ERROR; - /* Need to handle being moved via an ancestor. */ ancestor_relpath = local_relpath; - for (i = relpath_depth(local_relpath); i > working_op_depth; --i) + for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i) { const char *ancestor_moved_to; @@ -12584,56 +13159,30 @@ follow_moved_to(apr_array_header_t **moved_tos, SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath, - working_op_depth)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - SVN_ERR_ASSERT(have_row); + shadowing_op_depth)); + SVN_ERR(svn_sqlite__step_row(stmt)); + ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool); SVN_ERR(svn_sqlite__reset(stmt)); if (ancestor_moved_to) { - node_moved_to - = svn_relpath_join(ancestor_moved_to, - svn_relpath_skip_ancestor(ancestor_relpath, - local_relpath), - result_pool); - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_MOVED_HERE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to, - relpath_depth(ancestor_moved_to))); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (!have_row) - ancestor_moved_to = NULL; - else if (!svn_sqlite__column_int(stmt, 0)) - { - svn_wc__db_status_t presence - = svn_sqlite__column_token(stmt, 1, presence_map); - if (presence != svn_wc__db_status_not_present) - ancestor_moved_to = NULL; - else - { - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (!have_row && !svn_sqlite__column_int(stmt, 0)) - ancestor_moved_to = NULL; - } - } - SVN_ERR(svn_sqlite__reset(stmt)); - if (!ancestor_moved_to) - break; - /* verify repos_path points back? */ - } - if (ancestor_moved_to) - { struct svn_wc__db_moved_to_t *moved_to; + node_moved_to + = svn_relpath_join(ancestor_moved_to, + svn_relpath_skip_ancestor(ancestor_relpath, + local_relpath), + result_pool); + moved_to = apr_palloc(result_pool, sizeof(*moved_to)); - moved_to->op_depth = working_op_depth; + moved_to->op_depth = shadowing_op_depth; moved_to->local_relpath = node_moved_to; APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to; - SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to), - repos_path, revision, wcroot, node_moved_to, - result_pool, scratch_pool)); + SVN_ERR(follow_moved_to(wcroot, node_moved_to, + relpath_depth(ancestor_moved_to), + moved_tos, result_pool, scratch_pool)); + break; } } @@ -12661,96 +13210,91 @@ svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos, sizeof(struct svn_wc__db_moved_to_t *)); /* ### Wrap in a transaction */ - SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM, - wcroot, local_relpath, - result_pool, scratch_pool)); + SVN_WC__DB_WITH_TXN(follow_moved_to(wcroot, local_relpath, 0, moved_tos, + result_pool, scratch_pool), + wcroot); /* ### Convert moved_to to abspath */ return SVN_NO_ERROR; } -/* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by - examining the lowest working node above OP_DEPTH. The output paths - are NULL if there is no move, otherwise: +svn_error_t * +svn_wc__db_scan_moved_to_internal(const char **move_src_relpath, + const char **move_dst_relpath, + const char **delete_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + int delete_op_depth; + const char *relpath = local_relpath; + const char *dst_relpath; - *MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH. + SVN_ERR_ASSERT(local_relpath[0]); /* Not valid on the WC root */ - *MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of - the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH - if LOCAL_RELPATH is the root of the move. + if (move_src_relpath) + *move_src_relpath = NULL; + if (move_dst_relpath) + *move_dst_relpath = NULL; + if (delete_relpath) + *delete_relpath = NULL; - *MOVE_SRC_ROOT_RELPATH: the root of the move source. For moves - inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH. + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_OP_DEPTH_MOVED_TO)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth)); - *MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that - contains the move. For moves inside deletes this is the root of - the delete, for other moves this is the root of the move. + SVN_ERR(svn_sqlite__step(&have_row, stmt)); - Given a path A/B/C with A/B moved to X then for A/B/C + if (!have_row) + { + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, + svn_sqlite__reset(stmt), + _("Node '%s' is not shadowed"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + } - MOVE_DST_RELPATH is X/C - MOVE_DST_OP_ROOT_RELPATH is X - MOVE_SRC_ROOT_RELPATH is A/B - MOVE_SRC_OP_ROOT_RELPATH is A/B + delete_op_depth = svn_sqlite__column_int(stmt, 0); + dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); - If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH - and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH - changes to A. + SVN_ERR(svn_sqlite__reset(stmt)); - ### Think about combining with scan_deletion? Also with - ### scan_addition to get moved-to for replaces? Do we need to - ### return the op-root of the move source, i.e. A/B in the example - ### above? */ -svn_error_t * -svn_wc__db_op_depth_moved_to(const char **move_dst_relpath, - const char **move_dst_op_root_relpath, - const char **move_src_root_relpath, - const char **move_src_op_root_relpath, - int op_depth, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - int delete_op_depth; - const char *relpath = local_relpath; + while (!dst_relpath && have_row) + { + relpath = svn_relpath_dirname(relpath, scratch_pool); - *move_dst_relpath = *move_dst_op_root_relpath = NULL; - *move_src_root_relpath = *move_src_op_root_relpath = NULL; + if (relpath_depth(relpath) < delete_op_depth) + break; - do - { SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_LOWEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth)); + STMT_SELECT_DEPTH_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, + delete_op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) - { - delete_op_depth = svn_sqlite__column_int(stmt, 0); - *move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3, - result_pool); - if (*move_dst_op_root_relpath) - *move_src_root_relpath = apr_pstrdup(result_pool, relpath); - } + dst_relpath = svn_sqlite__column_text(stmt, 13, scratch_pool); + SVN_ERR(svn_sqlite__reset(stmt)); - if (!*move_dst_op_root_relpath) - relpath = svn_relpath_dirname(relpath, scratch_pool); } - while (!*move_dst_op_root_relpath - && have_row && delete_op_depth <= relpath_depth(relpath)); - if (*move_dst_op_root_relpath) + if (dst_relpath) { - *move_dst_relpath - = svn_relpath_join(*move_dst_op_root_relpath, - svn_relpath_skip_ancestor(relpath, local_relpath), - result_pool); - while (delete_op_depth < relpath_depth(relpath)) - relpath = svn_relpath_dirname(relpath, scratch_pool); - *move_src_op_root_relpath = apr_pstrdup(result_pool, relpath); + if (move_src_relpath) + *move_src_relpath = apr_pstrdup(result_pool, relpath); + + if (move_dst_relpath) + *move_dst_relpath = apr_pstrdup(result_pool, dst_relpath); + + if (delete_relpath) + *delete_relpath = svn_relpath_prefix(local_relpath, delete_op_depth, + result_pool); } return SVN_NO_ERROR; @@ -12763,7 +13307,7 @@ svn_error_t * svn_wc__db_base_moved_to(const char **move_dst_abspath, const char **move_dst_op_root_abspath, const char **move_src_root_abspath, - const char **move_src_op_root_abspath, + const char **delete_abspath, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -12771,47 +13315,54 @@ svn_wc__db_base_moved_to(const char **move_dst_abspath, { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; - const char *move_dst_relpath, *move_dst_op_root_relpath; - const char *move_src_root_relpath, *move_src_op_root_relpath; + const char *dst_root_relpath; + const char *src_root_relpath, *delete_relpath; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath, - &move_dst_op_root_relpath, - &move_src_root_relpath, - &move_src_op_root_relpath, - 0 /* BASE op-depth */, - wcroot, local_relpath, - scratch_pool, scratch_pool), + SVN_WC__DB_WITH_TXN(svn_wc__db_scan_moved_to_internal(&src_root_relpath, + &dst_root_relpath, + &delete_relpath, + wcroot, local_relpath, + 0 /* BASE */, + scratch_pool, + scratch_pool), wcroot); if (move_dst_abspath) - *move_dst_abspath - = move_dst_relpath - ? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool) - : NULL; + *move_dst_abspath = + dst_root_relpath + ? svn_dirent_join(wcroot->abspath, + svn_dirent_join( + dst_root_relpath, + svn_relpath_skip_ancestor(src_root_relpath, + local_relpath), + scratch_pool), + result_pool) + : NULL; if (move_dst_op_root_abspath) - *move_dst_op_root_abspath - = move_dst_op_root_relpath - ? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool) - : NULL; + *move_dst_op_root_abspath = + dst_root_relpath + ? svn_dirent_join(wcroot->abspath, dst_root_relpath, result_pool) + : NULL; if (move_src_root_abspath) - *move_src_root_abspath - = move_src_root_relpath - ? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool) - : NULL; + *move_src_root_abspath = + src_root_relpath + ? svn_dirent_join(wcroot->abspath, src_root_relpath, result_pool) + : NULL; - if (move_src_op_root_abspath) - *move_src_op_root_abspath - = move_src_op_root_relpath - ? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool) - : NULL; + if (delete_abspath) + *delete_abspath = + delete_relpath + ? svn_dirent_join(wcroot->abspath, delete_relpath, result_pool) + : NULL; return SVN_NO_ERROR; } @@ -12834,6 +13385,7 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, SDB_FILE, NULL, SVN_INVALID_REVNUM, svn_depth_unknown, TRUE /* exclusive */, + 0 /* timeout */, wc_db->state_pool, scratch_pool)); SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot, @@ -12841,7 +13393,6 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, dir_abspath), *sdb, *wc_id, FORMAT_FROM_SDB, FALSE /* auto-upgrade */, - FALSE /* enforce_empty_wq */, wc_db->state_pool, scratch_pool)); /* The WCROOT is complete. Stash it into DB. */ @@ -12850,191 +13401,6 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, return SVN_NO_ERROR; } - -svn_error_t * -svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb, - const char *dir_relpath, - apr_hash_t *cache_values, - apr_pool_t *scratch_pool) -{ - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - apr_int64_t wc_id; - apr_hash_index_t *hi; - svn_sqlite__stmt_t *stmt; - - SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool)); - - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_UPDATE_BASE_NODE_DAV_CACHE)); - - /* Iterate over all the wcprops, writing each one to the wc_db. */ - for (hi = apr_hash_first(scratch_pool, cache_values); - hi; - hi = apr_hash_next(hi)) - { - const char *name = svn__apr_hash_index_key(hi); - apr_hash_t *props = svn__apr_hash_index_val(hi); - const char *local_relpath; - - svn_pool_clear(iterpool); - - local_relpath = svn_relpath_join(dir_relpath, name, iterpool); - - SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); - SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool)); - SVN_ERR(svn_sqlite__step_done(stmt)); - } - - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb, - const char *dir_abspath, - const char *local_relpath, - apr_hash_t *base_props, - apr_hash_t *revert_props, - apr_hash_t *working_props, - int original_format, - apr_int64_t wc_id, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - int top_op_depth = -1; - int below_op_depth = -1; - svn_wc__db_status_t top_presence; - svn_wc__db_status_t below_presence; - int affected_rows; - - /* ### working_props: use set_props_txn. - ### if working_props == NULL, then skip. what if they equal the - ### pristine props? we should probably do the compare here. - ### - ### base props go into WORKING_NODE if avail, otherwise BASE. - ### - ### revert only goes into BASE. (and WORKING better be there!) - - Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a - file was deleted, then a copy (potentially with props) was disallowed - and could not replace the deletion. An addition *could* be performed, - but that would never bring its own props. - - 1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a - bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT - construct a REVERT_PROPS if the target had no props. Thus, reverting - the delete/copy would see no REVERT_PROPS to restore, leaving the - props from the copy source intact, and appearing as if they are (now) - the base props for the previously-deleted file. (wc corruption) - - 1.4.6 ensured that an empty REVERT_PROPS would be established at all - times. See issue 2530, and r861670 as starting points. - - We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine - the handling of our inputs, relative to the state of this node. - */ - - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - { - top_op_depth = svn_sqlite__column_int(stmt, 0); - top_presence = svn_sqlite__column_token(stmt, 3, presence_map); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - { - below_op_depth = svn_sqlite__column_int(stmt, 0); - below_presence = svn_sqlite__column_token(stmt, 3, presence_map); - } - } - SVN_ERR(svn_sqlite__reset(stmt)); - - /* Detect the buggy scenario described above. We cannot upgrade this - working copy if we have no idea where BASE_PROPS should go. */ - if (original_format > SVN_WC__NO_REVERT_FILES - && revert_props == NULL - && top_op_depth != -1 - && top_presence == svn_wc__db_status_normal - && below_op_depth != -1 - && below_presence != svn_wc__db_status_not_present) - { - /* There should be REVERT_PROPS, so it appears that we just ran into - the described bug. Sigh. */ - return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, - _("The properties of '%s' are in an " - "indeterminate state and cannot be " - "upgraded. See issue #2530."), - svn_dirent_local_style( - svn_dirent_join(dir_abspath, local_relpath, - scratch_pool), scratch_pool)); - } - - /* Need at least one row, or two rows if there are revert props */ - if (top_op_depth == -1 - || (below_op_depth == -1 && revert_props)) - return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, - _("Insufficient NODES rows for '%s'"), - svn_dirent_local_style( - svn_dirent_join(dir_abspath, local_relpath, - scratch_pool), scratch_pool)); - - /* one row, base props only: upper row gets base props - two rows, base props only: lower row gets base props - two rows, revert props only: lower row gets revert props - two rows, base and revert props: upper row gets base, lower gets revert */ - - - if (revert_props || below_op_depth == -1) - { - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_UPDATE_NODE_PROPS)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", - wc_id, local_relpath, top_op_depth)); - SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool)); - SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); - - SVN_ERR_ASSERT(affected_rows == 1); - } - - if (below_op_depth != -1) - { - apr_hash_t *props = revert_props ? revert_props : base_props; - - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_UPDATE_NODE_PROPS)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", - wc_id, local_relpath, below_op_depth)); - SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool)); - SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); - - SVN_ERR_ASSERT(affected_rows == 1); - } - - /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */ - if (working_props != NULL - && base_props != NULL) - { - apr_array_header_t *diffs; - - SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool)); - - if (diffs->nelts == 0) - working_props = NULL; /* No differences */ - } - - if (working_props != NULL) - { - SVN_ERR(set_actual_props(wc_id, local_relpath, working_props, - sdb, scratch_pool)); - } - - return SVN_NO_ERROR; -} - svn_error_t * svn_wc__db_upgrade_insert_external(svn_wc__db_t *db, const char *local_abspath, @@ -13110,28 +13476,15 @@ svn_wc__db_upgrade_insert_external(svn_wc__db_t *db, } svn_error_t * -svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id, - svn_sqlite__db_t *sdb, - const char *repos_root_url, - apr_pool_t *scratch_pool) +svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot, + const svn_skel_t *work_item, + apr_pool_t *scratch_pool) { - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY)); - SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - - if (!have_row) - return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt), - _("Repository '%s' not found in the database"), - repos_root_url); - - *repos_id = svn_sqlite__column_int64(stmt, 0); - return svn_error_trace(svn_sqlite__reset(stmt)); + /* Add the work item(s) to the WORK_QUEUE. */ + return svn_error_trace(add_work_items(wcroot->sdb, work_item, + scratch_pool)); } - svn_error_t * svn_wc__db_wq_add(svn_wc__db_t *db, const char *wri_abspath, @@ -13244,8 +13597,8 @@ wq_record(svn_wc__db_wcroot_t *wcroot, for (hi = apr_hash_first(scratch_pool, record_map); hi; hi = apr_hash_next(hi)) { - const char *local_abspath = svn__apr_hash_index_key(hi); - const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi); + const char *local_abspath = apr_hash_this_key(hi); + const svn_io_dirent2_t *dirent = apr_hash_this_val(hi); const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath); @@ -13458,7 +13811,7 @@ svn_wc__db_temp_get_all_access(svn_wc__db_t *db, hi; hi = apr_hash_next(hi)) { - const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); + const svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); /* This is highly redundant, 'cause the same WCROOT will appear many times in dir_data. */ @@ -13645,6 +13998,8 @@ svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files, svn_error_t * svn_wc__db_read_conflict(svn_skel_t **conflict, + svn_node_kind_t *kind, + apr_hash_t **props, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -13658,14 +14013,16 @@ svn_wc__db_read_conflict(svn_skel_t **conflict, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot, - local_relpath, + return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, kind, props, + wcroot, local_relpath, result_pool, scratch_pool)); } svn_error_t * svn_wc__db_read_conflict_internal(svn_skel_t **conflict, + svn_node_kind_t *kind, + apr_hash_t **props, svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_pool_t *result_pool, @@ -13674,6 +14031,11 @@ svn_wc__db_read_conflict_internal(svn_skel_t **conflict, svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; + if (kind) + *kind = svn_node_none; + if (props) + *props = NULL; + /* Check if we have a conflict in ACTUAL */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_ACTUAL_NODE)); @@ -13681,58 +14043,91 @@ svn_wc__db_read_conflict_internal(svn_skel_t **conflict, SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (! have_row) + if (have_row) { - /* Do this while stmt is still open to avoid closing the sqlite - transaction and then reopening. */ - svn_sqlite__stmt_t *stmt_node; - svn_error_t *err; + apr_size_t cfl_len; + const void *cfl_data; - err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb, - STMT_SELECT_NODE_INFO); + /* svn_skel__parse doesn't copy data, so store in result_pool */ + cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool); - if (err) - stmt_node = NULL; + if (cfl_data) + *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool); else - err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id, - local_relpath); + *conflict = NULL; - if (!err) - err = svn_sqlite__step(&have_row, stmt_node); + if (props) + { + svn_error_t *err; - if (stmt_node) - err = svn_error_compose_create(err, - svn_sqlite__reset(stmt_node)); + err = svn_error_trace(svn_sqlite__column_properties(props, stmt, 1, + result_pool, + scratch_pool)); - SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + } + } + else + *conflict = NULL; - if (have_row) + SVN_ERR(svn_sqlite__reset(stmt)); + + if (!have_row || kind || (props && !*props)) + { + svn_error_t *err = NULL; + svn_boolean_t have_info = FALSE; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_NODE_INFO)); + + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, + local_relpath)); + + SVN_ERR(svn_sqlite__step(&have_info, stmt)); + + if (have_info) { - *conflict = NULL; - return SVN_NO_ERROR; + if (kind) + { + svn_wc__db_status_t status; + int op_depth = svn_sqlite__column_int(stmt, 0); + + status = svn_sqlite__column_token(stmt, 3, presence_map); + + if (op_depth > 0) + err = convert_to_working_status(&status, status); + + if (!err && (status == svn_wc__db_status_normal + || status == svn_wc__db_status_added + || status == svn_wc__db_status_deleted + || status == svn_wc__db_status_incomplete)) + { + *kind = svn_sqlite__column_token(stmt, 4, kind_map); + } + } + + /* Need props, and no props in ACTUAL? */ + if (!err && (props && !*props)) + { + err = svn_sqlite__column_properties(props, stmt, 14, + result_pool, scratch_pool); + } } - return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, - _("The node '%s' was not found."), + SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); + + if (!have_row && !have_info) + { + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The node '%s' was not found."), path_for_error_message(wcroot, local_relpath, scratch_pool)); + } } - { - apr_size_t cfl_len; - const void *cfl_data; - - /* svn_skel__parse doesn't copy data, so store in result_pool */ - cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool); - - if (cfl_data) - *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool); - else - *conflict = NULL; - - return svn_error_trace(svn_sqlite__reset(stmt)); - } + return SVN_NO_ERROR; } @@ -13749,6 +14144,7 @@ svn_wc__db_read_kind(svn_node_kind_t *kind, const char *local_relpath; svn_sqlite__stmt_t *stmt_info; svn_boolean_t have_info; + svn_wc__db_status_t status; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); @@ -13780,12 +14176,27 @@ svn_wc__db_read_kind(svn_node_kind_t *kind, } } + status = svn_sqlite__column_token(stmt_info, 3, presence_map); + + if (show_deleted && status == svn_wc__db_status_base_deleted) + { + /* Let's return the kind of what is really deleted insead of what + we have cached in the base-deleted record */ + + SVN_ERR(svn_sqlite__step(&have_info, stmt_info)); + + if (!have_info) + { + /* No lower layer deleted? Database inconsistency! */ + *kind = svn_node_none; + return svn_error_trace(svn_sqlite__reset(stmt_info)); + } + } + if (!(show_deleted && show_hidden)) { int op_depth = svn_sqlite__column_int(stmt_info, 0); svn_boolean_t report_none = FALSE; - svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3, - presence_map); if (op_depth > 0) SVN_ERR(convert_to_working_status(&status, status)); @@ -13821,38 +14232,6 @@ svn_wc__db_read_kind(svn_node_kind_t *kind, return svn_error_trace(svn_sqlite__reset(stmt_info)); } - -svn_error_t * -svn_wc__db_node_hidden(svn_boolean_t *hidden, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - svn_wc__db_status_t status; - - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, - local_abspath, scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - - SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - - *hidden = (status == svn_wc__db_status_server_excluded - || status == svn_wc__db_status_not_present - || status == svn_wc__db_status_excluded); - - return SVN_NO_ERROR; -} - - svn_error_t * svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot, svn_wc__db_t *db, @@ -14017,7 +14396,7 @@ svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath, wcroot->abspath, svn_wc_get_adm_dir(scratch_pool), WCROOT_TEMPDIR_RELPATH, - NULL); + SVN_VA_NULL); return SVN_NO_ERROR; } @@ -14046,6 +14425,7 @@ wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, int levels_to_lock, svn_boolean_t steal_lock, + svn_boolean_t enforce_empty_wq, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; @@ -14075,6 +14455,9 @@ wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot, scratch_pool)); } + if (enforce_empty_wq) + SVN_ERR(svn_wc__db_verify_no_work(wcroot->sdb)); + /* Check if there are nodes locked below the new lock root */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); @@ -14101,8 +14484,9 @@ wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot, /* Check if we are the lock owner, because we should be able to extend our lock. */ - err = wclock_owns_lock(&own_lock, wcroot, lock_relpath, - TRUE, scratch_pool); + err = svn_wc__db_wclock_owns_lock_internal(&own_lock, wcroot, + lock_relpath, + TRUE, scratch_pool); if (err) SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt))); @@ -14190,7 +14574,7 @@ wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot, err = svn_sqlite__insert(NULL, stmt); if (err) return svn_error_createf(SVN_ERR_WC_LOCKED, err, - _("Working copy '%s' locked"), + _("Failed to lock working copy '%s'."), path_for_error_message(wcroot, local_relpath, scratch_pool)); @@ -14249,7 +14633,7 @@ svn_wc__db_wclock_obtain(svn_wc__db_t *db, SVN_WC__DB_WITH_TXN( wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock, - scratch_pool), + db->enforce_empty_wq, scratch_pool), wcroot); return SVN_NO_ERROR; } @@ -14440,12 +14824,12 @@ svn_wc__db_wclock_release(svn_wc__db_t *db, /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead of DB+LOCAL_ABSPATH. */ -static svn_error_t * -wclock_owns_lock(svn_boolean_t *own_lock, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_boolean_t exact, - apr_pool_t *scratch_pool) +svn_error_t * +svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t exact, + apr_pool_t *scratch_pool) { apr_array_header_t *owned_locks; int lock_level; @@ -14512,8 +14896,8 @@ svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock, VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact, - scratch_pool)); + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(own_lock, wcroot, local_relpath, + exact, scratch_pool)); return SVN_NO_ERROR; } @@ -14629,6 +15013,87 @@ svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db, return SVN_NO_ERROR; } +/* Helper for svn_wc__db_op_make_copy_internal */ +static svn_error_t * +db_move_moved_to(svn_wc__db_wcroot_t *wcroot, + const char *src1_relpath, + int src1_op_depth, + const char *src2_relpath, + int src2_op_depth, + const char *dst_relpath, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + int affected_rows; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_MOVED_TO_RELPATH)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + src1_relpath, src1_op_depth)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + + if (affected_rows == 1) + { + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_UPDATE_MOVED_TO_RELPATH)); + SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id, + src2_relpath, src2_op_depth, + dst_relpath)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + } + if (affected_rows != 1) + return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL); + + return SVN_NO_ERROR; +} + +static svn_error_t * +db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int new_shadow_layer, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + new_shadow_layer)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + while (have_row) + { + int del_op_depth; + const char *src_relpath; + const char *dst_relpath; + svn_error_t *err; + + svn_pool_clear(iterpool); + + del_op_depth = svn_sqlite__column_int(stmt, 0); + src_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool); + + err = svn_error_trace( + db_move_moved_to( + wcroot, + src_relpath, del_op_depth, + src_relpath, new_shadow_layer, + dst_relpath, iterpool)); + + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + + SVN_ERR(svn_sqlite__reset(stmt)); + + return SVN_NO_ERROR; +} + /* The body of svn_wc__db_temp_op_make_copy(). This is used by the update editor when deleting a base node tree would be a @@ -14666,104 +15131,224 @@ svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db, static svn_error_t * make_copy_txn(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - int op_depth, - const svn_skel_t *conflicts, - const svn_skel_t *work_items, + apr_int64_t last_repos_id, + const char *last_repos_relpath, + svn_revnum_t last_revision, + int last_op_depth, + svn_boolean_t shadowed, + int root_shadow_depth, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - svn_boolean_t add_working_base_deleted = FALSE; - svn_boolean_t remove_working = FALSE; - const apr_array_header_t *children; - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - int i; - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_LOWEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); + svn_boolean_t have_row = FALSE; + svn_revnum_t revision; + apr_int64_t repos_id; + const char *repos_relpath; + svn_node_kind_t kind; + int op_depth = relpath_depth(local_relpath); - if (have_row) + if (last_op_depth != op_depth) { - svn_wc__db_status_t working_status; - int working_op_depth; - - working_status = svn_sqlite__column_token(stmt, 1, presence_map); - working_op_depth = svn_sqlite__column_int(stmt, 0); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_DEPTH_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); SVN_ERR(svn_sqlite__reset(stmt)); + if (have_row) + shadowed = TRUE; + } - SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal - || working_status == svn_wc__db_status_base_deleted - || working_status == svn_wc__db_status_not_present - || working_status == svn_wc__db_status_incomplete); + SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision, + &repos_relpath, &repos_id, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + wcroot, local_relpath, + scratch_pool, scratch_pool)); - /* Only change nodes in the layers where we are creating the copy. - Deletes in higher layers will just apply to the copy */ - if (working_op_depth <= op_depth) - { - add_working_base_deleted = TRUE; + if (last_repos_relpath + && repos_id == last_repos_id + && revision == last_revision) + { + const char *name = svn_relpath_skip_ancestor(last_repos_relpath, + repos_relpath); - if (working_status == svn_wc__db_status_base_deleted) - remove_working = TRUE; - } + if (name && strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0) + op_depth = last_op_depth; } - else - SVN_ERR(svn_sqlite__reset(stmt)); - if (remove_working) + /* Can we add a new copy node at the wanted op-depth? */ + if (!have_row || op_depth == last_op_depth) { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_DELETE_LOWEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step_done(stmt)); - } + int i; - if (add_working_base_deleted) - { - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_DELETE_FROM_BASE)); + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_INSERT_WORKING_NODE_FROM_BASE_COPY)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, op_depth)); SVN_ERR(svn_sqlite__step_done(stmt)); + + if (shadowed) + SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind, + op_depth, scratch_pool)); + + if (kind == svn_node_dir) + { + const apr_array_header_t *children; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(gather_children(&children, wcroot, local_relpath, + STMT_SELECT_OP_DEPTH_CHILDREN, 0, + scratch_pool, iterpool)); + + for (i = 0; i < children->nelts; i++) + { + const char *name = APR_ARRAY_IDX(children, i, const char *); + const char *copy_relpath; + + svn_pool_clear(iterpool); + + copy_relpath = svn_relpath_join(local_relpath, name, iterpool); + + SVN_ERR(make_copy_txn(wcroot, copy_relpath, + repos_id, repos_relpath, revision, + op_depth, shadowed, root_shadow_depth, + scratch_pool)); + } + svn_pool_destroy(iterpool); + } } else { + /* Auch... we can't make a copy of whatever comes deeper, as this + op-depth is already filled by something else. Let's hope + the user doesn't mind. + + Luckily we know that the moves are already moved to the shadowing + layer, so we can just remove dangling base-deletes if there are + any. + */ + /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */ + SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath, + root_shadow_depth, + scratch_pool)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_INSERT_WORKING_NODE_FROM_BASE_COPY)); + STMT_DELETE_WORKING_BASE_DELETE)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); + last_op_depth)); + SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + last_op_depth)); SVN_ERR(svn_sqlite__step_done(stmt)); } - /* Get the BASE children, as WORKING children don't need modifications */ - SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, - 0, scratch_pool, iterpool)); + /* Insert a not-present node to mark that we don't know what exists here. - for (i = 0; i < children->nelts; i++) + We do this last (after recursing), to allow the move fix-up code to + see the original moves. */ + if (last_op_depth > 0 && last_op_depth != op_depth) { - const char *name = APR_ARRAY_IDX(children, i, const char *); - const char *copy_relpath; + insert_working_baton_t iwb; - svn_pool_clear(iterpool); + blank_iwb(&iwb); + iwb.presence = svn_wc__db_status_not_present; + iwb.op_depth = last_op_depth; - copy_relpath = svn_relpath_join(local_relpath, name, iterpool); + iwb.original_repos_id = repos_id; + iwb.original_repos_relpath = repos_relpath; + iwb.original_revnum = revision; + iwb.kind = kind; - SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL, - iterpool)); + SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool)); } - SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath, - iterpool), - svn_depth_empty, iterpool)); + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t move_move_info, + const svn_skel_t *conflicts, + const svn_skel_t *work_items, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + int op_depth = -1; + + /* The update editor is supposed to call this function when there is + no working node for LOCAL_ABSPATH. */ + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_WORKING_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + op_depth = svn_sqlite__column_int(stmt, 0); + SVN_ERR(svn_sqlite__reset(stmt)); + + if (have_row) + { + if (op_depth == relpath_depth(local_relpath)) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("Modification of '%s' already exists"), + path_for_error_message(wcroot, + local_relpath, + scratch_pool)); + + /* We have a working layer, but not one at the op-depth of local-relpath, + so we can create a copy by just copying the lower layer */ + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_COPY_OP_DEPTH_RECURSIVE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath, + op_depth, relpath_depth(local_relpath))); + SVN_ERR(svn_sqlite__step_done(stmt)); + } + else + { + int affected_rows; + + op_depth = relpath_depth(local_relpath); + /* We don't allow copies to contain server-excluded nodes; + the update editor is going to have to bail out. */ + SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, + scratch_pool)); + + /* Insert a shadowing layer */ + SVN_ERR(svn_sqlite__get_statement( + &stmt, wcroot->sdb, + STMT_INSERT_DELETE_FROM_NODE_RECURSIVE)); + + /* As we are keeping whatever is below, move the*/ + + SVN_ERR(svn_sqlite__bindf(stmt, "isdd", + wcroot->wc_id, local_relpath, + 0, op_depth)); + SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); + SVN_ERR_ASSERT(affected_rows > 0); + + if (!move_move_info) + SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath, + op_depth, scratch_pool)); + + + SVN_ERR(make_copy_txn(wcroot, local_relpath, + INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM, + op_depth, FALSE, op_depth, + scratch_pool)); + } if (conflicts) SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, - conflicts, iterpool)); + conflicts, scratch_pool)); - SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool)); - - svn_pool_destroy(iterpool); + SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool)); return SVN_NO_ERROR; } @@ -14778,8 +15363,6 @@ svn_wc__db_op_make_copy(svn_wc__db_t *db, { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); @@ -14787,30 +15370,15 @@ svn_wc__db_op_make_copy(svn_wc__db_t *db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - /* The update editor is supposed to call this function when there is - no working node for LOCAL_ABSPATH. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - SVN_ERR(svn_sqlite__reset(stmt)); - if (have_row) - return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, - _("Modification of '%s' already exists"), - path_for_error_message(wcroot, - local_relpath, - scratch_pool)); - - /* We don't allow copies to contain server-excluded nodes; - the update editor is going to have to bail out. */ - SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool)); - SVN_WC__DB_WITH_TXN( - make_copy_txn(wcroot, local_relpath, - relpath_depth(local_relpath), conflicts, work_items, - scratch_pool), + svn_wc__db_op_make_copy_internal(wcroot, local_relpath, FALSE, + conflicts, work_items, + scratch_pool), wcroot); + SVN_ERR(flush_entries(wcroot, local_abspath, + svn_depth_infinity, scratch_pool)); + return SVN_NO_ERROR; } @@ -15017,7 +15585,7 @@ has_switched_subtrees(svn_boolean_t *is_switched, does not match the given trailing URL then the whole working copy is switched. */ - SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb, + SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot, repos_id, scratch_pool)); url = svn_path_url_add_component2(repos_root_url, repos_relpath, scratch_pool); @@ -15108,17 +15676,14 @@ svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees, return SVN_NO_ERROR; } -/* Like svn_wc__db_has_local_mods(), +/* Like svn_wc__db_has_db_mods(), * but accepts a WCROOT/LOCAL_RELPATH pair. * ### This needs a DB as well as a WCROOT/RELPATH pair... */ static svn_error_t * -has_local_mods(svn_boolean_t *is_modified, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_wc__db_t *db, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) +has_db_mods(svn_boolean_t *is_modified, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; @@ -15130,9 +15695,6 @@ has_local_mods(svn_boolean_t *is_modified, SVN_ERR(svn_sqlite__step(is_modified, stmt)); SVN_ERR(svn_sqlite__reset(stmt)); - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - if (! *is_modified) { /* Check for property modifications. */ @@ -15142,96 +15704,6 @@ has_local_mods(svn_boolean_t *is_modified, /* If this query returns a row, the working copy is modified. */ SVN_ERR(svn_sqlite__step(is_modified, stmt)); SVN_ERR(svn_sqlite__reset(stmt)); - - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - } - - if (! *is_modified) - { - apr_pool_t *iterpool = NULL; - svn_boolean_t have_row; - - /* Check for text modifications. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_BASE_FILES_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - iterpool = svn_pool_create(scratch_pool); - while (have_row) - { - const char *node_abspath; - svn_filesize_t recorded_size; - apr_time_t recorded_time; - svn_boolean_t skip_check = FALSE; - svn_error_t *err; - - if (cancel_func) - { - err = cancel_func(cancel_baton); - if (err) - return svn_error_trace(svn_error_compose_create( - err, - svn_sqlite__reset(stmt))); - } - - svn_pool_clear(iterpool); - - node_abspath = svn_dirent_join(wcroot->abspath, - svn_sqlite__column_text(stmt, 0, - iterpool), - iterpool); - - recorded_size = get_recorded_size(stmt, 1); - recorded_time = svn_sqlite__column_int64(stmt, 2); - - if (recorded_size != SVN_INVALID_FILESIZE - && recorded_time != 0) - { - const svn_io_dirent2_t *dirent; - - err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE, - iterpool, iterpool); - if (err) - return svn_error_trace(svn_error_compose_create( - err, - svn_sqlite__reset(stmt))); - - if (dirent->kind != svn_node_file) - { - *is_modified = TRUE; /* Missing or obstruction */ - break; - } - else if (dirent->filesize == recorded_size - && dirent->mtime == recorded_time) - { - /* The file is not modified */ - skip_check = TRUE; - } - } - - if (! skip_check) - { - err = svn_wc__internal_file_modified_p(is_modified, - db, node_abspath, - FALSE, iterpool); - - if (err) - return svn_error_trace(svn_error_compose_create( - err, - svn_sqlite__reset(stmt))); - - if (*is_modified) - break; - } - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - if (iterpool) - svn_pool_destroy(iterpool); - - SVN_ERR(svn_sqlite__reset(stmt)); } return SVN_NO_ERROR; @@ -15239,12 +15711,10 @@ has_local_mods(svn_boolean_t *is_modified, svn_error_t * -svn_wc__db_has_local_mods(svn_boolean_t *is_modified, - svn_wc__db_t *db, - const char *local_abspath, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) +svn_wc__db_has_db_mods(svn_boolean_t *is_modified, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; @@ -15256,9 +15726,8 @@ svn_wc__db_has_local_mods(svn_boolean_t *is_modified, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath, - db, cancel_func, cancel_baton, - scratch_pool)); + return svn_error_trace(has_db_mods(is_modified, wcroot, local_relpath, + scratch_pool)); } @@ -15275,8 +15744,6 @@ revision_status_txn(svn_revnum_t *min_revision, svn_wc__db_t *db, const char *trail_url, svn_boolean_t committed, - svn_cancel_func_t cancel_func, - void *cancel_baton, apr_pool_t *scratch_pool) { svn_error_t *err; @@ -15296,16 +15763,10 @@ revision_status_txn(svn_revnum_t *min_revision, SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot, local_relpath, committed, scratch_pool)); - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - /* Determine sparseness. */ SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot, local_relpath, scratch_pool)); - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - /* Check for switched nodes. */ { err = has_switched_subtrees(is_switched, wcroot, local_relpath, @@ -15321,12 +15782,8 @@ revision_status_txn(svn_revnum_t *min_revision, } } - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - - /* Check for local mods. */ - SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db, - cancel_func, cancel_baton, scratch_pool)); + /* Check for db mods. */ + SVN_ERR(has_db_mods(is_modified, wcroot, local_relpath, scratch_pool)); return SVN_NO_ERROR; } @@ -15342,8 +15799,6 @@ svn_wc__db_revision_status(svn_revnum_t *min_revision, const char *local_abspath, const char *trail_url, svn_boolean_t committed, - svn_cancel_func_t cancel_func, - void *cancel_baton, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; @@ -15360,7 +15815,7 @@ svn_wc__db_revision_status(svn_revnum_t *min_revision, revision_status_txn(min_revision, max_revision, is_sparse_checkout, is_modified, is_switched, wcroot, local_relpath, db, - trail_url, committed, cancel_func, cancel_baton, + trail_url, committed, scratch_pool), wcroot); return SVN_NO_ERROR; @@ -15406,7 +15861,7 @@ svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens, if (child_repos_id != last_repos_id) { svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url, - NULL, wcroot->sdb, + NULL, wcroot, child_repos_id, scratch_pool); @@ -15529,6 +15984,69 @@ svn_wc__db_verify(svn_wc__db_t *db, return SVN_NO_ERROR; } + +svn_error_t * +svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot, + svn_wc__db_verify_cb_t callback, + void *baton, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + svn_error_t *err = NULL; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_STATIC_VERIFY)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + + while (have_row) + { + const char *local_relpath; + int op_depth = svn_sqlite__column_int(stmt, 1); + int id = svn_sqlite__column_int(stmt, 2); + const char *msg; + + svn_pool_clear(iterpool); + + local_relpath = svn_sqlite__column_text(stmt, 0, iterpool); + msg = svn_sqlite__column_text(stmt, 3, scratch_pool); + + err = callback(baton, wcroot->abspath, local_relpath, op_depth, + id, msg, iterpool); + + if (err) + break; + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + + svn_pool_destroy(iterpool); + + return svn_error_trace( + svn_error_compose_create(err, svn_sqlite__reset(stmt))); +} + +svn_error_t * +svn_wc__db_verify_db_full(svn_wc__db_t *db, + const char *wri_abspath, + svn_wc__db_verify_cb_t callback, + void *baton, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + wri_abspath, scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + return svn_error_trace( + svn_wc__db_verify_db_full_internal(wcroot, callback, baton, + scratch_pool)); +} + svn_error_t * svn_wc__db_bump_format(int *result_format, svn_boolean_t *bumped_format, @@ -15549,6 +16067,7 @@ svn_wc__db_bump_format(int *result_format, err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE, svn_sqlite__mode_readwrite, TRUE, /* exclusive */ + 0, /* default timeout */ NULL, /* my statements */ scratch_pool, scratch_pool); if (err) @@ -15608,3 +16127,504 @@ svn_wc__db_vacuum(svn_wc__db_t *db, return SVN_NO_ERROR; } + +/* Item queued with svn_wc__db_commit_queue_add */ +typedef struct commit_queue_item_t +{ + const char *local_relpath; + svn_boolean_t recurse; /* Use legacy recursion */ + svn_boolean_t committed; /* Process the node as committed */ + svn_boolean_t remove_lock; /* Remove existing lock on node */ + svn_boolean_t remove_changelist; /* Remove changelist on node */ + + /* The pristine text checksum. NULL if the old value should be kept + and for directories */ + const svn_checksum_t *new_sha1_checksum; + + apr_hash_t *new_dav_cache; /* New DAV cache for the node */ +} commit_queue_item_t; + +/* The queue definition for vn_wc__db_create_commit_queue, + svn_wc__db_commit_queue_add and finally svn_wc__db_process_commit_queue */ +struct svn_wc__db_commit_queue_t +{ + svn_wc__db_wcroot_t *wcroot; /* Wcroot for ITEMS */ + apr_array_header_t *items; /* List of commit_queue_item_t* */ + svn_boolean_t have_recurse; /* Is one or more item[x]->recurse TRUE? */ +}; + +/* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the + working copy specified with WRI_ABSPATH */ +svn_error_t * +svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue, + svn_wc__db_t *db, + const char *wri_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + svn_wc__db_commit_queue_t *q; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); + + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + wri_abspath, result_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); + + q = apr_pcalloc(result_pool, sizeof(*q)); + + SVN_ERR_ASSERT(wcroot->sdb); + + q->wcroot = wcroot; + q->items = apr_array_make(result_pool, 64, + sizeof(commit_queue_item_t*)); + q->have_recurse = FALSE; + + *queue = q; + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue, + const char *local_abspath, + svn_boolean_t recurse, + svn_boolean_t is_commited, + svn_boolean_t remove_lock, + svn_boolean_t remove_changelist, + const svn_checksum_t *new_sha1_checksum, + apr_hash_t *new_dav_cache, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + commit_queue_item_t *cqi; + const char *local_relpath; + + local_relpath = svn_dirent_skip_ancestor(queue->wcroot->abspath, + local_abspath); + + if (! local_relpath) + return svn_error_createf( + SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The path '%s' is not in the working copy '%s'"), + svn_dirent_local_style(local_abspath, scratch_pool), + svn_dirent_local_style(queue->wcroot->abspath, scratch_pool)); + + cqi = apr_pcalloc(result_pool, sizeof(*cqi)); + cqi->local_relpath = local_relpath; + cqi->recurse = recurse; + cqi->committed = is_commited; + cqi->remove_lock = remove_lock; + cqi->remove_changelist = remove_changelist; + cqi->new_sha1_checksum = new_sha1_checksum; + cqi->new_dav_cache = new_dav_cache; + + queue->have_recurse |= recurse; + + APR_ARRAY_PUSH(queue->items, commit_queue_item_t *) = cqi; + return SVN_NO_ERROR; +} + +/*** Finishing updates and commits. ***/ + +/* Post process an item that is committed in the repository. Collapse layers into + * BASE. Queue work items that will finish a commit of the file or directory + * LOCAL_ABSPATH in DB: + */ +static svn_error_t * +process_committed_leaf(svn_wc__db_t *db, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t via_recurse, + svn_wc__db_status_t status, + svn_node_kind_t kind, + svn_boolean_t prop_mods, + const svn_checksum_t *old_checksum, + svn_revnum_t new_revnum, + apr_time_t new_changed_date, + const char *new_changed_author, + apr_hash_t *new_dav_cache, + svn_boolean_t remove_lock, + svn_boolean_t remove_changelist, + const svn_checksum_t *checksum, + apr_pool_t *scratch_pool) +{ + svn_revnum_t new_changed_rev = new_revnum; + svn_skel_t *work_item = NULL; + + { + const char *lock_relpath; + svn_boolean_t locked; + + if (kind == svn_node_dir) + lock_relpath = local_relpath; + else + lock_relpath = svn_relpath_dirname(local_relpath, scratch_pool); + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, + lock_relpath, FALSE, + scratch_pool)); + + if (!locked) + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("No write-lock in '%s'"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + SVN_ERR(flush_entries(wcroot, lock_relpath, svn_depth_empty, + scratch_pool)); + } + + if (status == svn_wc__db_status_not_present) + { + /* We are committing the leaf of a copy operation. + We leave the not-present marker to allow pulling in excluded + children of a copy. + + The next update will remove the not-present marker. */ + + return SVN_NO_ERROR; + } + + SVN_ERR_ASSERT(status == svn_wc__db_status_normal + || status == svn_wc__db_status_incomplete + || status == svn_wc__db_status_added + || status == svn_wc__db_status_deleted); + + if (kind != svn_node_dir + && status != svn_wc__db_status_deleted) + { + /* If we sent a delta (meaning: post-copy modification), + then this file will appear in the queue and so we should have + its checksum already. */ + if (checksum == NULL) + { + /* It was copied and not modified. We must have a text + base for it. And the node should have a checksum. */ + SVN_ERR_ASSERT(old_checksum != NULL); + + checksum = old_checksum; + + /* Is the node completely unmodified and are we recursing? */ + if (via_recurse && !prop_mods) + { + /* If a copied node itself is not modified, but the op_root of + the copy is committed we have to make sure that changed_rev, + changed_date and changed_author don't change or the working + copy used for committing will show different last modified + information then a clean checkout of exactly the same + revisions. (Issue #3676) */ + + SVN_ERR(svn_wc__db_read_info_internal( + NULL, NULL, NULL, NULL, NULL, + &new_changed_rev, + &new_changed_date, + &new_changed_author, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, + wcroot, local_relpath, + scratch_pool, scratch_pool)); + } + } + + SVN_ERR(svn_wc__wq_build_file_commit(&work_item, + db, svn_dirent_join(wcroot->abspath, + local_relpath, + scratch_pool), + prop_mods, + scratch_pool, scratch_pool)); + } + + /* The new text base will be found in the pristine store by its checksum. */ + SVN_ERR(commit_node(wcroot, local_relpath, + new_revnum, new_changed_rev, + new_changed_date, new_changed_author, + checksum, + new_dav_cache, + !remove_changelist, + !remove_lock, + work_item, + scratch_pool)); + + return SVN_NO_ERROR; +} + +/** Internal helper for svn_wc_process_committed_queue2(). + * Bump a commit item, collapsing local changes with the new repository + * information to a new BASE node. + * + * @a new_date is the (server-side) date of the new revision, or 0. + * + * @a rev_author is the (server-side) author of the new + * revision; it may be @c NULL. + * + * @a new_dav_cache is a hash of all the new dav properties for LOCAL_RELPATH. + * + * If @a remove_lock is set, release any user locks on @a + * local_abspath; otherwise keep them during processing. + * + * If @a remove_changelist is set, clear any changeset assignments + * from @a local_abspath; otherwise, keep such assignments. + * + * If @a new_sha1_checksum is non-NULL, use it to identify the node's pristine + * text. + * + * Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly + * recursive commit operation. (Part of the legacy recurse handling) + */ +static svn_error_t * +process_committed_internal(svn_wc__db_t *db, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t recurse, + svn_boolean_t top_of_recurse, + svn_revnum_t new_revnum, + apr_time_t new_date, + const char *rev_author, + apr_hash_t *new_dav_cache, + svn_boolean_t remove_lock, + svn_boolean_t remove_changelist, + const svn_checksum_t *new_sha1_checksum, + apr_hash_t *items_by_relpath, + apr_pool_t *scratch_pool) +{ + svn_wc__db_status_t status; + svn_node_kind_t kind; + const svn_checksum_t *old_checksum; + svn_boolean_t prop_mods; + + SVN_ERR(svn_wc__db_read_info_internal(&status, &kind, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &old_checksum, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, &prop_mods, NULL, NULL, NULL, + wcroot, local_relpath, + scratch_pool, scratch_pool)); + + /* NOTE: be wary of making crazy semantic changes in this function, since + svn_wc_process_committed4() calls this. */ + + SVN_ERR(process_committed_leaf(db, wcroot, local_relpath, !top_of_recurse, + status, kind, prop_mods, old_checksum, + new_revnum, new_date, rev_author, + new_dav_cache, + remove_lock, remove_changelist, + new_sha1_checksum, + scratch_pool)); + + /* Only check for recursion on nodes that have children */ + if (kind != svn_node_dir + || status == svn_wc__db_status_not_present + || status == svn_wc__db_status_excluded + || status == svn_wc__db_status_server_excluded + /* Node deleted -> then no longer a directory */ + || status == svn_wc__db_status_deleted) + { + return SVN_NO_ERROR; + } + + if (recurse) + { + const apr_array_header_t *children; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + int i; + + /* Read PATH's entries; this is the absolute path. */ + SVN_ERR(gather_children(&children, wcroot, local_relpath, + STMT_SELECT_NODE_CHILDREN, -1, + scratch_pool, iterpool)); + + /* Recursively loop over all children. */ + for (i = 0; i < children->nelts; i++) + { + const char *name = APR_ARRAY_IDX(children, i, const char *); + const char *this_relpath; + const commit_queue_item_t *cqi; + + svn_pool_clear(iterpool); + + this_relpath = svn_dirent_join(local_relpath, name, iterpool); + + new_sha1_checksum = NULL; + cqi = svn_hash_gets(items_by_relpath, this_relpath); + + if (cqi != NULL) + new_sha1_checksum = cqi->new_sha1_checksum; + + /* Recurse. Pass NULL for NEW_DAV_CACHE, because the + ones present in the current call are only applicable to + this one committed item. */ + SVN_ERR(process_committed_internal( + db, wcroot, this_relpath, + TRUE /* recurse */, + FALSE /* top_of_recurse */, + new_revnum, new_date, + rev_author, + NULL /* new_dav_cache */, + FALSE /* remove_lock */, + remove_changelist, + new_sha1_checksum, + items_by_relpath, + iterpool)); + } + + svn_pool_destroy(iterpool); + } + + return SVN_NO_ERROR; +} + +/* Return TRUE if any item of QUEUE is a parent of ITEM and will be + processed recursively, return FALSE otherwise. + + The algorithmic complexity of this search implementation is O(queue + length), but it's quite quick. +*/ +static svn_boolean_t +have_recursive_parent(const apr_array_header_t *all_items, + const commit_queue_item_t *item, + apr_pool_t *scratch_pool) +{ + const char *local_relpath = item->local_relpath; + int i; + + for (i = 0; i < all_items->nelts; i++) + { + const commit_queue_item_t *qi + = APR_ARRAY_IDX(all_items, i, const commit_queue_item_t *); + + if (qi == item) + continue; + + if (qi->recurse && svn_relpath_skip_ancestor(qi->local_relpath, + local_relpath)) + { + return TRUE; + } + } + + return FALSE; +} + +/* Compare function for svn_sort__array */ +static int +compare_queue_items(const void *v1, + const void *v2) +{ + const commit_queue_item_t *cqi1 + = *(const commit_queue_item_t **)v1; + const commit_queue_item_t *cqi2 + = *(const commit_queue_item_t **)v2; + + return svn_path_compare_paths(cqi1->local_relpath, cqi2->local_relpath); +} + +/* Internal, locked version of svn_wc__db_process_commit_queue */ +static svn_error_t * +db_process_commit_queue(svn_wc__db_t *db, + svn_wc__db_commit_queue_t *queue, + svn_revnum_t new_revnum, + apr_time_t new_date, + const char *new_author, + apr_pool_t *scratch_pool) +{ + apr_hash_t *items_by_relpath = NULL; + int j; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + svn_sort__array(queue->items, compare_queue_items); + + if (queue->have_recurse) + { + items_by_relpath = apr_hash_make(scratch_pool); + + for (j = 0; j < queue->items->nelts; j++) + { + commit_queue_item_t *cqi + = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *); + + svn_hash_sets(items_by_relpath, cqi->local_relpath, cqi); + } + } + + for (j = 0; j < queue->items->nelts; j++) + { + commit_queue_item_t *cqi + = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *); + + svn_pool_clear(iterpool); + + /* Skip this item if it is a child of a recursive item, because it has + been (or will be) accounted for when that recursive item was (or + will be) processed. */ + if (queue->have_recurse && have_recursive_parent(queue->items, cqi, + iterpool)) + continue; + + if (!cqi->committed) + { + if (cqi->remove_lock) + { + svn_skel_t *work_item; + + SVN_ERR(svn_wc__wq_build_sync_file_flags( + &work_item, + db, + svn_dirent_join( + queue->wcroot->abspath, + cqi->local_relpath, + iterpool), + iterpool, iterpool)); + + lock_remove_txn(queue->wcroot, cqi->local_relpath, work_item, + iterpool); + } + if (cqi->remove_changelist) + SVN_ERR(svn_wc__db_op_set_changelist(db, + svn_dirent_join( + queue->wcroot->abspath, + cqi->local_relpath, + iterpool), + NULL, NULL, + svn_depth_empty, + NULL, NULL, /* notify */ + NULL, NULL, /* cancel */ + iterpool)); + } + else + { + SVN_ERR(process_committed_internal( + db, queue->wcroot, cqi->local_relpath, + cqi->recurse, + TRUE /* top_of_recurse */, + new_revnum, new_date, new_author, + cqi->new_dav_cache, + cqi->remove_lock, + cqi->remove_changelist, + cqi->new_sha1_checksum, + items_by_relpath, + iterpool)); + } + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_process_commit_queue(svn_wc__db_t *db, + svn_wc__db_commit_queue_t *queue, + svn_revnum_t new_revnum, + apr_time_t new_date, + const char *new_author, + apr_pool_t *scratch_pool) +{ + SVN_WC__DB_WITH_TXN(db_process_commit_queue(db, queue, + new_revnum, new_date, + new_author, scratch_pool), + queue->wcroot); + + return SVN_NO_ERROR; +} diff --git a/subversion/libsvn_wc/wc_db.h b/subversion/libsvn_wc/wc_db.h index 0fcce5e..a947be0 100644 --- a/subversion/libsvn_wc/wc_db.h +++ b/subversion/libsvn_wc/wc_db.h @@ -20,7 +20,7 @@ * ==================================================================== * @endcopyright * - * @file svn_wc_db.h + * @file wc_db.h * @brief The Subversion Working Copy Library - Metadata/Base-Text Support * * Requires: @@ -412,9 +412,6 @@ svn_wc__db_get_wcroot(const char **wcroot_abspath, If DAV_CACHE is not NULL, sets LOCAL_ABSPATH's dav cache to the specified data. - If CONFLICT is not NULL, then it describes a conflict for this node. The - node will be record as conflicted (in ACTUAL). - If UPDATE_ACTUAL_PROPS is TRUE, set the properties store NEW_ACTUAL_PROPS as the new set of properties in ACTUAL. If NEW_ACTUAL_PROPS is NULL or when the value of NEW_ACTUAL_PROPS matches NEW_PROPS, store NULL in @@ -424,6 +421,9 @@ svn_wc__db_get_wcroot(const char **wcroot_abspath, svn_prop_inherited_item_t * structures that is set as the base node's inherited_properties. + If CONFLICT is not NULL, then it describes a conflict for this node. The + node will be record as conflicted (in ACTUAL). + Any work items that are necessary as part of this node construction may be passed in WORK_ITEMS. @@ -444,10 +444,10 @@ svn_wc__db_base_add_directory(svn_wc__db_t *db, const apr_array_header_t *children, svn_depth_t depth, apr_hash_t *dav_cache, - const svn_skel_t *conflict, svn_boolean_t update_actual_props, apr_hash_t *new_actual_props, apr_array_header_t *new_iprops, + const svn_skel_t *conflict, const svn_skel_t *work_items, apr_pool_t *scratch_pool); @@ -676,13 +676,15 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t *db, const svn_skel_t *work_items, apr_pool_t *scratch_pool); +/* Remove a node and all its descendants from the BASE tree. This can + be done in two modes: -/* Remove a node and all its descendants from the BASE tree. This handles - the deletion of a tree from the update editor and some file external - scenarios. + * Remove everything, scheduling wq operations to clean up + the working copy. (KEEP_WORKING = FALSE) - The node to remove is indicated by LOCAL_ABSPATH from the local - filesystem. + * Bump things to WORKING, so the BASE layer is free, but the working + copy unmodified, except that everything that was visible from + BASE is now a copy of what it used to be. (KEEP_WORKING = TRUE) This operation *installs* workqueue operations to update the local filesystem after the database operation. @@ -693,21 +695,11 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t *db, actual node will be removed if the actual node does not mark a conflict. - If KEEP_AS_WORKING is TRUE, then the base tree is copied to higher - layers as a copy of itself before deleting the BASE nodes. - - If KEEP_AS_WORKING is FALSE, and QUEUE_DELETES is TRUE, also queue - workqueue items to delete all in-wc representations that aren't - shadowed by higher layers. - (With KEEP_AS_WORKING TRUE, this is a no-op, as everything is - automatically shadowed by the created copy) - - If REMOVE_LOCKS is TRUE, all locks of this node and any subnodes - are also removed. This is to be done during commit of deleted nodes. - If NOT_PRESENT_REVISION specifies a valid revision a not-present - node is installed in BASE node with kind NOT_PRESENT_KIND after - deleting. + If MARK_NOT_PRESENT or MARK_EXCLUDED is TRUE, install a marker + of the specified type at the root of the now removed tree, with + either the specified revision (or in case of SVN_INVALID_REVNUM) + the original revision. If CONFLICT and/or WORK_ITEMS are passed they are installed as part of the operation, after the work items inserted by the operation @@ -716,10 +708,10 @@ svn_wc__db_base_add_not_present_node(svn_wc__db_t *db, svn_error_t * svn_wc__db_base_remove(svn_wc__db_t *db, const char *local_abspath, - svn_boolean_t keep_as_working, - svn_boolean_t queue_deletes, - svn_boolean_t remove_locks, - svn_revnum_t not_present_revision, + svn_boolean_t keep_working, + svn_boolean_t mark_not_present, + svn_boolean_t mark_excluded, + svn_revnum_t marker_revision, svn_skel_t *conflict, svn_skel_t *work_items, apr_pool_t *scratch_pool); @@ -953,33 +945,46 @@ svn_wc__db_pristine_read(svn_stream_t **contents, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Baton for svn_wc__db_pristine_install */ +typedef struct svn_wc__db_install_data_t + svn_wc__db_install_data_t; -/* Set *TEMP_DIR_ABSPATH to a directory in which the caller should create - a uniquely named file for later installation as a pristine text file. +/* Open a writable stream to a temporary text base, ready for installing + into the pristine store. Set *STREAM to the opened stream. The temporary + file will have an arbitrary unique name. Return as *INSTALL_DATA a baton + for eiter installing or removing the file - The directory is guaranteed to be one that svn_wc__db_pristine_install() - can use: specifically, one from which it can atomically move the file. + Arrange that, on stream closure, *MD5_CHECKSUM and *SHA1_CHECKSUM will be + set to the MD-5 and SHA-1 checksums respectively of that file. + MD5_CHECKSUM and/or SHA1_CHECKSUM may be NULL if not wanted. - Allocate *TEMP_DIR_ABSPATH in RESULT_POOL. */ + Allocate the new stream, path and checksums in RESULT_POOL. + */ svn_error_t * -svn_wc__db_pristine_get_tempdir(const char **temp_dir_abspath, - svn_wc__db_t *db, - const char *wri_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - +svn_wc__db_pristine_prepare_install(svn_stream_t **stream, + svn_wc__db_install_data_t **install_data, + svn_checksum_t **sha1_checksum, + svn_checksum_t **md5_checksum, + svn_wc__db_t *db, + const char *wri_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); -/* Install the file TEMPFILE_ABSPATH (which is sitting in a directory given by - svn_wc__db_pristine_get_tempdir()) into the pristine data store, to be - identified by the SHA-1 checksum of its contents, SHA1_CHECKSUM, and whose - MD-5 checksum is MD5_CHECKSUM. */ +/* Install the file created via svn_wc__db_pristine_prepare_install() into + the pristine data store, to be identified by the SHA-1 checksum of its + contents, SHA1_CHECKSUM, and whose MD-5 checksum is MD5_CHECKSUM. */ svn_error_t * -svn_wc__db_pristine_install(svn_wc__db_t *db, - const char *tempfile_abspath, +svn_wc__db_pristine_install(svn_wc__db_install_data_t *install_data, const svn_checksum_t *sha1_checksum, const svn_checksum_t *md5_checksum, apr_pool_t *scratch_pool); +/* Removes the temporary data created by svn_wc__db_pristine_prepare_install + when the pristine won't be installed. */ +svn_error_t * +svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data, + apr_pool_t *scratch_pool); + /* Set *MD5_CHECKSUM to the MD-5 checksum of a pristine text identified by its SHA-1 checksum SHA1_CHECKSUM. Return an error @@ -1248,6 +1253,53 @@ svn_wc__db_committable_externals_below(apr_array_header_t **externals, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Opaque struct for svn_wc__db_create_commit_queue, svn_wc__db_commit_queue_add, + svn_wc__db_process_commit_queue */ +typedef struct svn_wc__db_commit_queue_t svn_wc__db_commit_queue_t; + +/* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the + working copy specified with WRI_ABSPATH */ +svn_error_t * +svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue, + svn_wc__db_t *db, + const char *wri_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Adds the specified path to the commit queue with the related information. + + See svn_wc_queue_committed4() for argument documentation. + + Note that this function currently DOESN'T copy the passed values to + RESULT_POOL, but expects them to be valid until processing. Otherwise the + only users memory requirements would +- double. + */ +svn_error_t * +svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue, + const char *local_abspath, + svn_boolean_t recurse, + svn_boolean_t is_commited, + svn_boolean_t remove_lock, + svn_boolean_t remove_changelist, + const svn_checksum_t *new_sha1_checksum, + apr_hash_t *new_dav_cache, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Process the items in QUEUE in a single transaction. Commit workqueue items + for items that need post processing. + + Implementation detail of svn_wc_process_committed_queue2(). + */ +svn_error_t * +svn_wc__db_process_commit_queue(svn_wc__db_t *db, + svn_wc__db_commit_queue_t *queue, + svn_revnum_t new_revnum, + apr_time_t new_date, + const char *new_author, + apr_pool_t *scratch_pool); + + /* Gets a mapping from const char * local abspaths of externals to the const char * local abspath of where they are defined for all externals defined at or below LOCAL_ABSPATH. @@ -1616,6 +1668,9 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t *db, * * At present only depth=empty and depth=infinity are supported. * + * If @a clear_changelists is FALSE then changelist information is kept, + * otherwise it is cleared. + * * This function populates the revert list that can be queried to * determine what was reverted. */ @@ -1623,6 +1678,7 @@ svn_error_t * svn_wc__db_op_revert(svn_wc__db_t *db, const char *local_abspath, svn_depth_t depth, + svn_boolean_t clear_changelists, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -1658,7 +1714,7 @@ typedef struct svn_wc__db_revert_list_copied_child_info_t { * Allocate *COPIED_CHILDREN and its elements in RESULT_POOL. * The elements are of type svn_wc__db_revert_list_copied_child_info_t. */ svn_error_t * -svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children, +svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -1963,6 +2019,7 @@ struct svn_wc__db_info_t { svn_boolean_t moved_here; /* Only on op-roots. */ svn_boolean_t file_external; + svn_boolean_t has_descendants; /* Is dir, or has tc descendants */ }; /* Return in *NODES a hash mapping name->struct svn_wc__db_info_t for @@ -1972,27 +2029,36 @@ struct svn_wc__db_info_t { The results include any path that was a child of a deleted directory that existed at LOCAL_ABSPATH, even if that directory is now scheduled to be replaced by the working node at LOCAL_ABSPATH. + + If BASE_TREE_ONLY is set, only information about the BASE tree + is returned. */ svn_error_t * svn_wc__db_read_children_info(apr_hash_t **nodes, apr_hash_t **conflicts, svn_wc__db_t *db, const char *dir_abspath, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool); /* Like svn_wc__db_read_children_info, but only gets an info node for the root - element. */ + element. + + If BASE_TREE_ONLY is set, only information about the BASE tree + is returned. */ svn_error_t * svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info, svn_wc__db_t *db, const char *local_abspath, + svn_boolean_t base_tree_only, apr_pool_t *result_pool, apr_pool_t *scratch_pool); /* Structure returned by svn_wc__db_read_walker_info. Only has the fields needed by svn_wc__internal_walk_children(). */ struct svn_wc__db_walker_info_t { + const char *name; svn_wc__db_status_t status; svn_node_kind_t kind; }; @@ -2053,11 +2119,10 @@ svn_wc__db_read_node_install_info(const char **wcroot_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* Return in *NODES a hash mapping name->struct svn_wc__db_walker_info_t for - the children of DIR_ABSPATH. "name" is the child's name relative to - DIR_ABSPATH, not an absolute path. */ +/* Return in *ITEMS an array of struct svn_wc__db_walker_info_t* for + the direct children of DIR_ABSPATH. */ svn_error_t * -svn_wc__db_read_children_walker_info(apr_hash_t **nodes, +svn_wc__db_read_children_walker_info(const apr_array_header_t **items, svn_wc__db_t *db, const char *dir_abspath, apr_pool_t *result_pool, @@ -2065,15 +2130,26 @@ svn_wc__db_read_children_walker_info(apr_hash_t **nodes, /** - * Set *URL to the corresponding url for LOCAL_ABSPATH. - * If the node is added, return the url it will have in the repository. + * Set *revision, *repos_relpath, *repos_root_url, *repos_uuid to + * the intended/commit location of LOCAL_ABSPATH. These arguments may be + * NULL if they are not needed. + * + * If the node is deleted, return the url it would have in the repository + * if it wouldn't be deleted. If the node is added return the url it will + * have in the repository, once committed. + * + * If the node is not added and has an existing repository location, set + * revision to its existing revision, otherwise to SVN_INVALID_REVNUM. */ svn_error_t * -svn_wc__db_read_url(const char **url, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); +svn_wc__db_read_repos_info(svn_revnum_t *revision, + const char **repos_relpath, + const char **repos_root_url, + const char **repos_uuid, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /* Set *PROPS to the properties of the node LOCAL_ABSPATH in the ACTUAL @@ -2114,16 +2190,16 @@ svn_wc__db_read_props_streamily(svn_wc__db_t *db, apr_pool_t *scratch_pool); -/* Set *PROPS to the properties of the node LOCAL_ABSPATH in the WORKING - tree (looking through to the BASE tree as required). - - ### *PROPS will set set to NULL in the following situations: - ### ... tbd. see props.c:svn_wc__get_pristine_props() +/* Set *PROPS to the base properties of the node at LOCAL_ABSPATH. *PROPS maps "const char *" names to "const svn_string_t *" values. If the node has no properties, set *PROPS to an empty hash. - If the node is not present, return an error. + If the base node is in a state that cannot have properties (such as + not-present or locally added without copy-from), return an error. + Allocate *PROPS and its keys and values in RESULT_POOL. + + See also svn_wc_get_pristine_props(). */ svn_error_t * svn_wc__db_read_pristine_props(apr_hash_t **props, @@ -2146,7 +2222,7 @@ svn_wc__db_read_pristine_props(apr_hash_t **props, * paths relative to the repository root URL for cached inherited * properties and absolute working copy paths otherwise. * - * If ACTUAL_PROPS is not NULL, then set *ACTUAL_PROPS to the actual + * If ACTUAL_PROPS is not NULL, then set *ACTUAL_PROPS to ALL the actual * properties stored on LOCAL_ABSPATH. * * Allocate @a *iprops in @a result_pool. Use @a scratch_pool @@ -2237,6 +2313,14 @@ svn_wc__db_read_children_of_working_node(const apr_array_header_t **children, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +svn_error_t * +svn_wc__db_base_read_not_present_children( + const apr_array_header_t **children, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + /* Like svn_wc__db_read_children_of_working_node(), except also include any path that was a child of a deleted directory that existed at LOCAL_ABSPATH, even if that directory is now scheduled to be replaced by @@ -2279,7 +2363,10 @@ svn_wc__db_get_conflict_marker_files(apr_hash_t **markers, apr_pool_t *scratch_pool); /* Read the conflict information recorded on LOCAL_ABSPATH in *CONFLICT, - an editable conflict skel. + an editable conflict skel. If kind is not NULL, also read the node kind + in *KIND. (SHOW_HIDDEN: false, SHOW_DELETED: true). If props is not NULL + read the actual properties in this value if they exist. (Set to NULL in case + the node is deleted, etc.) If the node exists, but does not have a conflict set *CONFLICT to NULL, otherwise return a SVN_ERR_WC_PATH_NOT_FOUND error. @@ -2288,6 +2375,8 @@ svn_wc__db_get_conflict_marker_files(apr_hash_t **markers, SCRATCH_POOL */ svn_error_t * svn_wc__db_read_conflict(svn_skel_t **conflict, + svn_node_kind_t *kind, + apr_hash_t **props, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -2323,16 +2412,6 @@ svn_wc__db_read_kind(svn_node_kind_t *kind, svn_boolean_t show_hidden, apr_pool_t *scratch_pool); - -/* An analog to svn_wc__entry_is_hidden(). Set *HIDDEN to TRUE if - LOCAL_ABSPATH in DB "is not present, and I haven't scheduled something - over the top of it." */ -svn_error_t * -svn_wc__db_node_hidden(svn_boolean_t *hidden, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *scratch_pool); - /* Checks if a node replaces a node in a different layer. Also check if it replaces a BASE (op_depth 0) node or just a node in a higher layer (a copy). Finally check if this is the root of the replacement, or if the replacement @@ -2444,11 +2523,6 @@ svn_wc__db_global_relocate(svn_wc__db_t *db, CHANGED_AUTHOR is the (server-side) author of CHANGED_REVISION. It may be NULL if the revprop is missing on the revision. - One or both of NEW_CHECKSUM and NEW_CHILDREN should be NULL. For new: - files: NEW_CHILDREN should be NULL - dirs: NEW_CHECKSUM should be NULL - symlinks: both should be NULL - WORK_ITEMS will be place into the work queue. */ svn_error_t * @@ -2459,7 +2533,6 @@ svn_wc__db_global_commit(svn_wc__db_t *db, apr_time_t changed_date, const char *changed_author, const svn_checksum_t *new_checksum, - const apr_array_header_t *new_children, apr_hash_t *new_dav_cache, svn_boolean_t keep_changelist, svn_boolean_t no_unlock, @@ -2540,6 +2613,9 @@ svn_wc__db_global_update(svn_wc__db_t *db, for pathnames contained in EXCLUDE_RELPATHS are not touched by this function. These pathnames should be paths relative to the wcroot. + If EMPTY_UPDATE is TRUE then no nodes at or below LOCAL_ABSPATH have been + affected by the update/switch yet. + If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute working copy paths to depth-first ordered arrays of svn_prop_inherited_item_t * structures. If LOCAL_ABSPATH exists in @@ -2556,6 +2632,7 @@ svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db, svn_revnum_t new_revision, apr_hash_t *exclude_relpaths, apr_hash_t *wcroot_iprops, + svn_boolean_t empty_update, svn_wc_notify_func2_t notify_func, void *notify_baton, apr_pool_t *scratch_pool); @@ -2605,10 +2682,12 @@ svn_wc__db_lock_add(svn_wc__db_t *db, apr_pool_t *scratch_pool); -/* Remove any lock for LOCAL_ABSPATH in DB. */ +/* Remove any lock for LOCAL_ABSPATH in DB and install WORK_ITEMS + (if not NULL) in DB */ svn_error_t * svn_wc__db_lock_remove(svn_wc__db_t *db, const char *local_abspath, + svn_skel_t *work_items, apr_pool_t *scratch_pool); @@ -2619,30 +2698,6 @@ svn_wc__db_lock_remove(svn_wc__db_t *db, @{ */ -/* Read a BASE node's repository information. - - For the BASE node implied by LOCAL_ABSPATH, its location in the repository - returned in *REPOS_ROOT_URL and *REPOS_UUID will be returned in - *REPOS_RELPATH. Any of the OUT parameters may be NULL, indicating no - interest in that piece of information. - - All returned data will be allocated in RESULT_POOL. All temporary - allocations will be made in SCRATCH_POOL. - - ### Either delete this function and use _base_get_info instead, or - ### add a 'revision' output to make a complete repository node location - ### and rename to not say 'scan', because it doesn't. -*/ -svn_error_t * -svn_wc__db_scan_base_repos(const char **repos_relpath, - const char **repos_root_url, - const char **repos_uuid, - svn_wc__db_t *db, - const char *local_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); - - /* Scan upwards for information about a known addition to the WORKING tree. IFF a node's status as returned by svn_wc__db_read_info() is @@ -2707,7 +2762,8 @@ svn_wc__db_scan_addition(svn_wc__db_status_t *status, apr_pool_t *scratch_pool); /* Scan the working copy for move information of the node LOCAL_ABSPATH. - * If LOCAL_ABSPATH return a SVN_ERR_WC_PATH_UNEXPECTED_STATUS error. + * If LOCAL_ABSPATH is not moved here return an + * SVN_ERR_WC_PATH_UNEXPECTED_STATUS error. * * If not NULL *MOVED_FROM_ABSPATH will be set to the previous location * of LOCAL_ABSPATH, before it or an ancestror was moved. @@ -2879,34 +2935,6 @@ svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb, const char *repos_uuid, apr_pool_t *scratch_pool); - -svn_error_t * -svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb, - const char *dir_relpath, - apr_hash_t *cache_values, - apr_pool_t *scratch_pool); - - -/* ### need much more docco - - ### this function should be called within a sqlite transaction. it makes - ### assumptions around this fact. - - Apply the various sets of properties to the database nodes based on - their existence/presence, the current state of the node, and the original - format of the working copy which provided these property sets. -*/ -svn_error_t * -svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb, - const char *dir_abspath, - const char *local_relpath, - apr_hash_t *base_props, - apr_hash_t *revert_props, - apr_hash_t *working_props, - int original_format, - apr_int64_t wc_id, - apr_pool_t *scratch_pool); - /* Simply insert (or replace) one row in the EXTERNALS table. */ svn_error_t * svn_wc__db_upgrade_insert_external(svn_wc__db_t *db, @@ -2921,20 +2949,6 @@ svn_wc__db_upgrade_insert_external(svn_wc__db_t *db, svn_revnum_t def_revision, apr_pool_t *scratch_pool); -/* Get the repository identifier corresponding to REPOS_ROOT_URL from the - database in SDB. The value is returned in *REPOS_ID. All allocations - are allocated in SCRATCH_POOL. - - NOTE: the row in REPOSITORY must exist. If not, then SVN_ERR_WC_DB_ERROR - is returned. - - ### unclear on whether/how this interface will stay/evolve. */ -svn_error_t * -svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id, - svn_sqlite__db_t *sdb, - const char *repos_root_url, - apr_pool_t *scratch_pool); - /* Upgrade the metadata concerning the WC at WCROOT_ABSPATH, in DB, * to the SVN_WC__VERSION format. * @@ -3075,10 +3089,6 @@ svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock, This operation always recursively removes all nodes at and below LOCAL_ABSPATH from NODES and ACTUAL. - If NOT_PRESENT_REVISION specifies a valid revision, leave a not_present - BASE node at local_abspath of the specified status and kind. - (Requires an existing BASE node before removing) - If DESTROY_WC is TRUE, this operation *installs* workqueue operations to update the local filesystem after the database operation. If DESTROY_CHANGES is FALSE, modified and unversioned files are left after running this @@ -3096,9 +3106,6 @@ svn_wc__db_op_remove_node(svn_boolean_t *left_changes, const char *local_abspath, svn_boolean_t destroy_wc, svn_boolean_t destroy_changes, - svn_revnum_t not_present_revision, - svn_wc__db_status_t not_present_status, - svn_node_kind_t not_present_kind, const svn_skel_t *conflict, const svn_skel_t *work_items, svn_cancel_func_t cancel_func, @@ -3192,9 +3199,21 @@ svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db, apr_pool_t *scratch_pool); -/* Copy the base tree at LOCAL_ABSPATH into the working tree as copy, - leaving any subtree additions and copies as-is. This allows the - base node tree to be removed. */ +/* When local_abspath has no WORKING layer, copy the base tree at + LOCAL_ABSPATH into the working tree as copy, leaving any subtree + additions and copies as-is. This may introduce multiple layers if + the tree is mixed revision. + + When local_abspath has a WORKING node, but is not an op-root, copy + all descendants at the same op-depth to the op-depth of local_abspath, + thereby turning this node in a copy of what was already there. + + Fails with a SVN_ERR_WC_PATH_UNEXPECTED_STATUS error if LOCAL_RELPATH + is already an op-root (as in that case it can't be copied as that + would overwrite what is already there). + + After this operation the copied layer (E.g. BASE) can be removed, without + the WORKING nodes chaning. Typical usecase: tree conflict handling */ svn_error_t * svn_wc__db_op_make_copy(svn_wc__db_t *db, const char *local_abspath, @@ -3249,7 +3268,8 @@ svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants, * * Indicate in *IS_SPARSE_CHECKOUT whether any of the nodes within * LOCAL_ABSPATH is sparse. - * Indicate in *IS_MODIFIED whether the working copy has local modifications. + * Indicate in *IS_MODIFIED whether the working copy has local modifications + * recorded for it in DB. * * Indicate in *IS_SWITCHED whether any node beneath LOCAL_ABSPATH * is switched. If TRAIL_URL is non-NULL, use it to determine if LOCAL_ABSPATH @@ -3270,8 +3290,6 @@ svn_wc__db_revision_status(svn_revnum_t *min_revision, const char *local_abspath, const char *trail_url, svn_boolean_t committed, - svn_cancel_func_t cancel_func, - void *cancel_baton, apr_pool_t *scratch_pool); /* Set *MIN_REVISION and *MAX_REVISION to the lowest and highest revision @@ -3332,16 +3350,13 @@ svn_wc__db_get_excluded_subtrees(apr_hash_t **server_excluded_subtrees, /* Indicate in *IS_MODIFIED whether the working copy has local modifications, * using DB. Use SCRATCH_POOL for temporary allocations. * - * This function provides a subset of the functionality of - * svn_wc__db_revision_status() and is more efficient if the caller - * doesn't need all information returned by svn_wc__db_revision_status(). */ + * This function does not check the working copy state, but is a lot more + * efficient than a full status walk. */ svn_error_t * -svn_wc__db_has_local_mods(svn_boolean_t *is_modified, - svn_wc__db_t *db, - const char *local_abspath, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool); +svn_wc__db_has_db_mods(svn_boolean_t *is_modified, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool); /* Verify the consistency of metadata concerning the WC that contains @@ -3369,27 +3384,31 @@ svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* Update a moved-away tree conflict victim at VICTIM_ABSPATH with changes - * brought in by the update operation which flagged the tree conflict. */ +/* Update a moved-away tree conflict victim LOCAL_ABSPATH, deleted in + DELETE_OP_ABSPATH with changes from the original location. */ svn_error_t * svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, - const char *victim_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, + const char *local_abspath, + const char *delete_op_abspath, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, apr_pool_t *scratch_pool); /* LOCAL_ABSPATH is moved to MOVE_DST_ABSPATH. MOVE_SRC_ROOT_ABSPATH * is the root of the move to MOVE_DST_OP_ROOT_ABSPATH. - * MOVE_SRC_OP_ROOT_ABSPATH is the op-root of the move; it's the same + * DELETE_ABSPATH is the op-root of the move; it's the same * as MOVE_SRC_ROOT_ABSPATH except for moves inside deletes when it is * the op-root of the delete. */ svn_error_t * svn_wc__db_base_moved_to(const char **move_dst_abspath, const char **move_dst_op_root_abspath, const char **move_src_root_abspath, - const char **move_src_op_root_abspath, + const char **delete_abspath, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *result_pool, @@ -3407,39 +3426,24 @@ svn_wc__db_vacuum(svn_wc__db_t *db, comment in resolve_conflict_on_node about combining with another function. */ svn_error_t * -svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool); - -/* Like svn_wc__db_resolve_delete_raise_moved_away this should be - combined. - - ### LOCAL_ABSPATH specifies the move origin, but the move origin - ### is not necessary unique enough. This function needs an op_root_abspath - ### argument to differentiate between different origins. - - ### See move_tests.py: move_many_update_delete for an example case. - */ -svn_error_t * -svn_wc__db_resolve_break_moved_away(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool); +svn_wc__db_op_raise_moved_away(svn_wc__db_t *db, + const char *local_abspath, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool); -/* Break moves for all moved-away children of LOCAL_ABSPATH, within - * a single transaction. - * - * ### Like svn_wc__db_resolve_delete_raise_moved_away this should be - * combined. */ +/* Breaks all moves of nodes that exist at or below LOCAL_ABSPATH as + shadowed (read: deleted) by the opration rooted at + delete_op_root_abspath. + */ svn_error_t * -svn_wc__db_resolve_break_moved_away_children(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool); +svn_wc__db_op_break_moved_away(svn_wc__db_t *db, + const char *local_abspath, + const char *delete_op_root_abspath, + svn_boolean_t mark_tc_resolved, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool); /* Set *REQUIRED_ABSPATH to the path that should be locked to ensure * that the lock covers all paths affected by resolving the conflicts @@ -3452,6 +3456,28 @@ svn_wc__required_lock_for_resolve(const char **required_abspath, apr_pool_t *scratch_pool); /* @} */ +typedef svn_error_t * (*svn_wc__db_verify_cb_t)(void *baton, + const char *wc_abspath, + const char *local_relpath, + int op_depth, + int id, + const char *description, + apr_pool_t *scratch_pool); + +/* Checks the database for FULL-correctness according to the spec. + + Note that typical 1.7-1.9 databases WILL PRODUCE warnings. + + This is mainly useful for WC-NG developers, as there will be + warnings without the database being corrupt +*/ +svn_error_t * +svn_wc__db_verify_db_full(svn_wc__db_t *db, + const char *wri_abspath, + svn_wc__db_verify_cb_t callback, + void *baton, + apr_pool_t *scratch_pool); + #ifdef __cplusplus } diff --git a/subversion/libsvn_wc/wc_db_pristine.c b/subversion/libsvn_wc/wc_db_pristine.c index d9dc8f3..9118d70 100644 --- a/subversion/libsvn_wc/wc_db_pristine.c +++ b/subversion/libsvn_wc/wc_db_pristine.c @@ -26,8 +26,11 @@ #define SVN_WC__I_AM_WC_DB #include "svn_pools.h" +#include "svn_io.h" #include "svn_dirent_uri.h" +#include "private/svn_io_private.h" + #include "wc.h" #include "wc_db.h" #include "wc-queries.h" @@ -67,7 +70,7 @@ get_pristine_fname(const char **pristine_abspath, wcroot_abspath, svn_wc_get_adm_dir(scratch_pool), PRISTINE_STORAGE_RELPATH, - NULL); + SVN_VA_NULL); /* We should have a valid checksum and (thus) a valid digest. */ SVN_ERR_ASSERT(hexdigest != NULL); @@ -78,14 +81,14 @@ get_pristine_fname(const char **pristine_abspath, subdir[2] = '\0'; hexdigest = apr_pstrcat(scratch_pool, hexdigest, PRISTINE_STORAGE_EXT, - (char *)NULL); + SVN_VA_NULL); /* The file is located at DIR/.svn/pristine/XX/XXYYZZ...svn-base */ *pristine_abspath = svn_dirent_join_many(result_pool, base_dir_abspath, subdir, hexdigest, - NULL); + SVN_VA_NULL); return SVN_NO_ERROR; } @@ -194,10 +197,20 @@ pristine_read_txn(svn_stream_t **contents, } /* Open the file as a readable stream. It will remain readable even when - * deleted from disk; APR guarantees that on Windows as well as Unix. */ + * deleted from disk; APR guarantees that on Windows as well as Unix. + * + * We also don't enable APR_BUFFERED on this file to maximize throughput + * e.g. for fulltext comparison. As we use SVN__STREAM_CHUNK_SIZE buffers + * where needed in streams, there is no point in having another layer of + * buffers. */ if (contents) - SVN_ERR(svn_stream_open_readonly(contents, pristine_abspath, - result_pool, scratch_pool)); + { + apr_file_t *file; + SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ, + APR_OS_DEFAULT, result_pool)); + *contents = svn_stream_from_aprfile2(file, FALSE, result_pool); + } + return SVN_NO_ERROR; } @@ -253,31 +266,9 @@ pristine_get_tempdir(svn_wc__db_wcroot_t *wcroot, { return svn_dirent_join_many(result_pool, wcroot->abspath, svn_wc_get_adm_dir(scratch_pool), - PRISTINE_TEMPDIR_RELPATH, (char *)NULL); + PRISTINE_TEMPDIR_RELPATH, SVN_VA_NULL); } -svn_error_t * -svn_wc__db_pristine_get_tempdir(const char **temp_dir_abspath, - svn_wc__db_t *db, - const char *wri_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - - SVN_ERR_ASSERT(temp_dir_abspath != NULL); - SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, - wri_abspath, scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - - *temp_dir_abspath = pristine_get_tempdir(wcroot, result_pool, scratch_pool); - return SVN_NO_ERROR; -} - - /* Install the pristine text described by BATON into the pristine store of * SDB. If it is already stored then just delete the new file * BATON->tempfile_abspath. @@ -290,7 +281,7 @@ svn_wc__db_pristine_get_tempdir(const char **temp_dir_abspath, static svn_error_t * pristine_install_txn(svn_sqlite__db_t *sdb, /* The path to the source file that is to be moved into place. */ - const char *tempfile_abspath, + svn_stream_t *install_stream, /* The target path for the file (within the pristine store). */ const char *pristine_abspath, /* The pristine text's SHA-1 checksum. */ @@ -299,10 +290,8 @@ pristine_install_txn(svn_sqlite__db_t *sdb, const svn_checksum_t *md5_checksum, apr_pool_t *scratch_pool) { - apr_finfo_t finfo; svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - svn_error_t *err; /* If this pristine text is already present in the store, just keep it: * delete the new one and return. */ @@ -310,6 +299,7 @@ pristine_install_txn(svn_sqlite__db_t *sdb, SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); SVN_ERR(svn_sqlite__reset(stmt)); + if (have_row) { #ifdef SVN_DEBUG @@ -317,8 +307,10 @@ pristine_install_txn(svn_sqlite__db_t *sdb, * ### We could check much more. */ { apr_finfo_t finfo1, finfo2; - SVN_ERR(svn_io_stat(&finfo1, tempfile_abspath, APR_FINFO_SIZE, - scratch_pool)); + + SVN_ERR(svn_stream__install_get_info(&finfo1, install_stream, APR_FINFO_SIZE, + scratch_pool)); + SVN_ERR(svn_io_stat(&finfo2, pristine_abspath, APR_FINFO_SIZE, scratch_pool)); if (finfo1.size != finfo2.size) @@ -333,84 +325,94 @@ pristine_install_txn(svn_sqlite__db_t *sdb, #endif /* Remove the temp file: it's already there */ - SVN_ERR(svn_io_remove_file2(tempfile_abspath, - FALSE /* ignore_enoent */, scratch_pool)); + SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool)); return SVN_NO_ERROR; } /* Move the file to its target location. (If it is already there, it is * an orphan file and it doesn't matter if we overwrite it.) */ - err = svn_io_file_rename(tempfile_abspath, pristine_abspath, - scratch_pool); + { + apr_finfo_t finfo; + SVN_ERR(svn_stream__install_get_info(&finfo, install_stream, APR_FINFO_SIZE, + scratch_pool)); + SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath, + TRUE, scratch_pool)); + + SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, + STMT_INSERT_PRISTINE)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); + SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); + SVN_ERR(svn_sqlite__insert(NULL, stmt)); + + SVN_ERR(svn_io_set_file_read_only(pristine_abspath, FALSE, scratch_pool)); + } - /* Maybe the directory doesn't exist yet? */ - if (err && APR_STATUS_IS_ENOENT(err->apr_err)) - { - svn_error_t *err2; + return SVN_NO_ERROR; +} - err2 = svn_io_dir_make(svn_dirent_dirname(pristine_abspath, - scratch_pool), - APR_OS_DEFAULT, scratch_pool); +struct svn_wc__db_install_data_t +{ + svn_wc__db_wcroot_t *wcroot; + svn_stream_t *inner_stream; +}; - if (err2) - /* Creating directory didn't work: Return all errors */ - return svn_error_trace(svn_error_compose_create(err, err2)); - else - /* We could create a directory: retry install */ - svn_error_clear(err); +svn_error_t * +svn_wc__db_pristine_prepare_install(svn_stream_t **stream, + svn_wc__db_install_data_t **install_data, + svn_checksum_t **sha1_checksum, + svn_checksum_t **md5_checksum, + svn_wc__db_t *db, + const char *wri_abspath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_wc__db_wcroot_t *wcroot; + const char *local_relpath; + const char *temp_dir_abspath; - SVN_ERR(svn_io_file_rename(tempfile_abspath, pristine_abspath, - scratch_pool)); - } - else - SVN_ERR(err); + SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath)); - SVN_ERR(svn_io_stat(&finfo, pristine_abspath, APR_FINFO_SIZE, - scratch_pool)); + SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, + wri_abspath, scratch_pool, scratch_pool)); + VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, - STMT_INSERT_PRISTINE)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool)); - SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); - SVN_ERR(svn_sqlite__insert(NULL, stmt)); + temp_dir_abspath = pristine_get_tempdir(wcroot, scratch_pool, scratch_pool); + + *install_data = apr_pcalloc(result_pool, sizeof(**install_data)); + (*install_data)->wcroot = wcroot; + + SVN_ERR_W(svn_stream__create_for_install(stream, + temp_dir_abspath, + result_pool, scratch_pool), + _("Unable to create pristine install stream")); + + (*install_data)->inner_stream = *stream; + + if (md5_checksum) + *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum, + svn_checksum_md5, FALSE, result_pool); + if (sha1_checksum) + *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum, + svn_checksum_sha1, FALSE, result_pool); return SVN_NO_ERROR; } - svn_error_t * -svn_wc__db_pristine_install(svn_wc__db_t *db, - const char *tempfile_abspath, +svn_wc__db_pristine_install(svn_wc__db_install_data_t *install_data, const svn_checksum_t *sha1_checksum, const svn_checksum_t *md5_checksum, apr_pool_t *scratch_pool) { - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - const char *wri_abspath; + svn_wc__db_wcroot_t *wcroot = install_data->wcroot; const char *pristine_abspath; - SVN_ERR_ASSERT(svn_dirent_is_absolute(tempfile_abspath)); SVN_ERR_ASSERT(sha1_checksum != NULL); SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1); SVN_ERR_ASSERT(md5_checksum != NULL); SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5); - /* ### this logic assumes that TEMPFILE_ABSPATH follows this pattern: - ### WCROOT_ABSPATH/COMPONENT/COMPONENT/TEMPFNAME - ### if we change this (see PRISTINE_TEMPDIR_RELPATH), then this - ### logic should change. */ - wri_abspath = svn_dirent_dirname( - svn_dirent_dirname( - svn_dirent_dirname(tempfile_abspath, scratch_pool), - scratch_pool), - scratch_pool); - - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, - wri_abspath, scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(get_pristine_fname(&pristine_abspath, wcroot->abspath, sha1_checksum, scratch_pool, scratch_pool)); @@ -419,7 +421,7 @@ svn_wc__db_pristine_install(svn_wc__db_t *db, * at the disk, to ensure no concurrent pristine install/delete txn. */ SVN_SQLITE__WITH_IMMEDIATE_TXN( pristine_install_txn(wcroot->sdb, - tempfile_abspath, pristine_abspath, + install_data->inner_stream, pristine_abspath, sha1_checksum, md5_checksum, scratch_pool), wcroot->sdb); @@ -427,6 +429,14 @@ svn_wc__db_pristine_install(svn_wc__db_t *db, return SVN_NO_ERROR; } +svn_error_t * +svn_wc__db_pristine_install_abort(svn_wc__db_install_data_t *install_data, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_stream__install_delete(install_data->inner_stream, + scratch_pool)); +} + svn_error_t * svn_wc__db_pristine_get_md5(const svn_checksum_t **md5_checksum, @@ -823,12 +833,44 @@ svn_wc__db_pristine_remove(svn_wc__db_t *db, } +/* Remove all unreferenced pristines in the WC DB in WCROOT. + * + * Look for pristine texts whose 'refcount' in the DB is zero, and remove + * them from the 'pristine' table and from disk. + * + * TODO: At least check that any zero refcount is really correct, before + * using it. See dev@ email thread "Pristine text missing - cleanup + * doesn't work", <http://svn.haxx.se/dev/archive-2013-04/0426.shtml>. + * + * TODO: Ideas for possible extra clean-up operations: + * + * * Check and correct all the refcounts. Identify any rows missing + * from the 'pristine' table. (Create a temporary index for speed + * if necessary?) + * + * * Check the checksums. (Very expensive to check them all, so find + * a way to not check them all.) + * + * * Check for pristine files missing from disk but referenced in the + * 'pristine' table. + * + * * Repair any pristine files missing from disk and/or rows missing + * from the 'pristine' table and/or bad checksums. Generally + * requires contacting the server, so requires support at a higher + * level than this function. + * + * * Identify any pristine text files on disk that are not referenced + * in the DB, and delete them. + * + * TODO: Provide feedback about any errors found and any corrections made. + */ static svn_error_t * pristine_cleanup_wcroot(svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_error_t *err = NULL; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); /* Find each unreferenced pristine in the DB and remove it. */ SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -838,16 +880,20 @@ pristine_cleanup_wcroot(svn_wc__db_wcroot_t *wcroot, svn_boolean_t have_row; const svn_checksum_t *sha1_checksum; + svn_pool_clear(iterpool); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); if (! have_row) break; SVN_ERR(svn_sqlite__column_checksum(&sha1_checksum, stmt, 0, - scratch_pool)); + iterpool)); err = pristine_remove_if_unreferenced(wcroot, sha1_checksum, - scratch_pool); + iterpool); } + svn_pool_destroy(iterpool); + return svn_error_trace( svn_error_compose_create(err, svn_sqlite__reset(stmt))); } diff --git a/subversion/libsvn_wc/wc_db_private.h b/subversion/libsvn_wc/wc_db_private.h index a4bf98f..a9d7b04 100644 --- a/subversion/libsvn_wc/wc_db_private.h +++ b/subversion/libsvn_wc/wc_db_private.h @@ -42,12 +42,16 @@ struct svn_wc__db_t { opened, and found to be not-current? */ svn_boolean_t verify_format; - /* Should we ensure the WORK_QUEUE is empty when a WCROOT is opened? */ + /* Should we ensure the WORK_QUEUE is empty when a DB is locked + * for writing? */ svn_boolean_t enforce_empty_wq; /* Should we open Sqlite databases EXCLUSIVE */ svn_boolean_t exclusive; + /* Busy timeout in ms., 0 for the libsvn_subr default. */ + apr_int32_t timeout; + /* Map a given working copy directory to its relevant data. const char *local_abspath -> svn_wc__db_wcroot_t *wcroot */ apr_hash_t *dir_data; @@ -122,7 +126,6 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot, apr_int64_t wc_id, int format, svn_boolean_t verify_format, - svn_boolean_t enforce_empty_wq, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -145,6 +148,9 @@ svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Return an error if the work queue in SDB is non-empty. */ +svn_error_t * +svn_wc__db_verify_no_work(svn_sqlite__db_t *sdb); /* Assert that the given WCROOT is usable. NOTE: the expression is multiply-evaluated!! */ @@ -192,7 +198,7 @@ svn_wc__db_util_fetch_wc_id(apr_int64_t *wc_id, /* Open a connection in *SDB to the WC database found in the WC metadata * directory inside DIR_ABSPATH, having the filename SDB_FNAME. * - * SMODE is passed to svn_sqlite__open(). + * SMODE, EXCLUSIVE and TIMEOUT are passed to svn_sqlite__open(). * * Register MY_STATEMENTS, or if that is null, the default set of WC DB * statements, as the set of statements to be prepared now and executed @@ -205,10 +211,18 @@ svn_wc__db_util_open_db(svn_sqlite__db_t **sdb, const char *sdb_fname, svn_sqlite__mode_t smode, svn_boolean_t exclusive, + apr_int32_t timeout, const char *const *my_statements, apr_pool_t *result_pool, apr_pool_t *scratch_pool); +/* Like svn_wc__db_wq_add() but taking WCROOT */ +svn_error_t * +svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot, + const svn_skel_t *work_item, + apr_pool_t *scratch_pool); + + /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */ svn_error_t * @@ -299,14 +313,40 @@ svn_wc__db_depth_get_info(svn_wc__db_status_t *status, apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* Look up REPOS_ID in SDB and set *REPOS_ROOT_URL and/or *REPOS_UUID to - its root URL and UUID respectively. If REPOS_ID is INVALID_REPOS_ID, +svn_error_t * +svn_wc__db_scan_addition_internal( + svn_wc__db_status_t *status, + const char **op_root_relpath_p, + const char **repos_relpath, + apr_int64_t *repos_id, + const char **original_repos_relpath, + apr_int64_t *original_repos_id, + svn_revnum_t *original_revision, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +svn_error_t * +svn_wc__db_scan_deletion_internal( + const char **base_del_relpath, + const char **moved_to_relpath, + const char **work_del_relpath, + const char **moved_to_op_root_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + + +/* Look up REPOS_ID in WCROOT->SDB and set *REPOS_ROOT_URL and/or *REPOS_UUID + to its root URL and UUID respectively. If REPOS_ID is INVALID_REPOS_ID, use NULL for both URL and UUID. Either or both output parameters may be NULL if not wanted. */ svn_error_t * svn_wc__db_fetch_repos_info(const char **repos_root_url, const char **repos_uuid, - svn_sqlite__db_t *sdb, + svn_wc__db_wcroot_t *wcroot, apr_int64_t repos_id, apr_pool_t *result_pool); @@ -314,6 +354,8 @@ svn_wc__db_fetch_repos_info(const char **repos_root_url, DB+LOCAL_ABSPATH, and outputting relpaths instead of abspaths. */ svn_error_t * svn_wc__db_read_conflict_internal(svn_skel_t **conflict, + svn_node_kind_t *kind, + apr_hash_t **props, svn_wc__db_wcroot_t *wcroot, const char *local_relpath, apr_pool_t *result_pool, @@ -330,23 +372,6 @@ svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot, /* Transaction handling */ -/* A callback which supplies WCROOTs and LOCAL_RELPATHs. */ -typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *scratch_pool); - - -/* Run CB_FUNC in a SQLite transaction with CB_BATON, using WCROOT and - LOCAL_RELPATH. If callbacks require additional information, they may - provide it using CB_BATON. */ -svn_error_t * -svn_wc__db_with_txn(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_wc__db_txn_callback_t cb_func, - void *cb_baton, - apr_pool_t *scratch_pool); - /* Evaluate the expression EXPR within a transaction. * * Begin a transaction in WCROOT's DB; evaluate the expression EXPR, which would @@ -369,79 +394,91 @@ svn_wc__db_with_txn(svn_wc__db_wcroot_t *wcroot, #define SVN_WC__DB_WITH_TXN4(expr1, expr2, expr3, expr4, wcroot) \ SVN_SQLITE__WITH_LOCK4(expr1, expr2, expr3, expr4, (wcroot)->sdb) +/* Update the single op-depth layer in the move destination subtree + rooted at DST_RELPATH to make it match the move source subtree + rooted at SRC_RELPATH. */ +svn_error_t * +svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_op_relpath, + int src_op_depth, + const char *dst_op_relpath, + svn_skel_t *conflict, + svn_skel_t *work_items, + apr_pool_t *scratch_pool); -/* Return CHILDREN mapping const char * names to svn_node_kind_t * for the - children of LOCAL_RELPATH at OP_DEPTH. */ +/* Like svn_wc__db_op_make_copy but with wcroot, local_relpath */ svn_error_t * -svn_wc__db_get_children_op_depth(apr_hash_t **children, - svn_wc__db_wcroot_t *wcroot, +svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - int op_depth, - apr_pool_t *result_pool, + svn_boolean_t move_move_info, + const svn_skel_t *conflicts, + const svn_skel_t *work_items, apr_pool_t *scratch_pool); -/* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH. +/* Extract the moved-to information for LOCAL_RELPATH as it existed + at OP-DEPTH. The output paths are optional and set to NULL + if there is no move, otherwise: - ### What about KIND and OP_DEPTH? KIND ought to be redundant; I'm - discussing on dev@ whether we can let that be null for presence - == base-deleted. OP_DEPTH is the op-depth of what, and why? - It is used to select the lowest working node higher than OP_DEPTH, - so, in terms of the API, OP_DEPTH means ...? + *MOVE_SRC_RELPATH: the path that was moved (LOCAL_RELPATH or one + of its ancestors) - Given a wc: + *MOVE_DST_RELPATH: The path *MOVE_SRC_RELPATH was moved to. - 0 1 2 3 4 - normal - A normal - A/B normal normal - A/B/C not-pres normal - A/B/C/D normal + *DELETE_RELPATH: The path at which LOCAL_RELPATH was removed ( + *MOVE_SRC_RELPATH or one of its ancestors) - That is checkout, delete A/B, copy a replacement A/B, delete copied - child A/B/C, add replacement A/B/C, add A/B/C/D. + Given a path A/B/C with A/B moved to X and A deleted then for A/B/C: - Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E - must extend the A/B deletion: + MOVE_SRC_RELPATH is A/B + MOVE_DST_RELPATH is X + DELETE_RELPATH is A - 0 1 2 3 4 - normal - A normal - A/B normal normal - A/B/C normal not-pres normal - A/B/C/D normal base-del normal - A/B/C/D/E normal base-del + X/C can be calculated if necessesary, like with the other + scan functions. - When adding a node if the parent has a higher working node then the - parent node is deleted (or replaced) and the delete must be extended - to cover new node. + This function returns SVN_ERR_WC_PATH_NOT_FOUND if LOCAL_RELPATH didn't + exist at OP_DEPTH, or when it is not shadowed. - In the example above A/B/C/D and A/B/C/D/E are the nodes that get - the extended delete, A/B/C is already deleted. - */ + ### Think about combining with scan_deletion? Also with + ### scan_addition to get moved-to for replaces? Do we need to + ### return the op-root of the move source, i.e. A/B in the example + ### above? */ svn_error_t * -svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_node_kind_t kind, - int op_depth, - apr_pool_t *scratch_pool); +svn_wc__db_scan_moved_to_internal(const char **move_src_relpath, + const char **move_dst_relpath, + const char **delete_relpath, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); +/* Like svn_wc__db_op_set_props, but updates ACTUAL_NODE directly without + comparing with the pristine properties, etc. +*/ svn_error_t * -svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot, +svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - int op_depth, + apr_hash_t *props, + svn_boolean_t clear_recorded_info, apr_pool_t *scratch_pool); svn_error_t * -svn_wc__db_op_depth_moved_to(const char **move_dst_relpath, - const char **move_dst_op_root_relpath, - const char **move_src_root_relpath, - const char **move_src_op_root_relpath, - int op_depth, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool); +svn_wc__db_read_props_internal(apr_hash_t **props, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + +/* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead + of DB+LOCAL_ABSPATH. */ +svn_error_t * +svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_boolean_t exact, + apr_pool_t *scratch_pool); /* Do a post-drive revision bump for the moved-away destination for any move sources under LOCAL_RELPATH. This is called from within @@ -455,12 +492,38 @@ svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool); /* Unbreak the move from LOCAL_RELPATH on op-depth in WCROOT, by making - the destination a normal copy */ + the destination DST_RELPATH a normal copy. SRC_OP_DEPTH is the op-depth + where the move_to information is stored */ svn_error_t * -svn_wc__db_resolve_break_moved_away_internal(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - int op_depth, - apr_pool_t *scratch_pool); +svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot, + const char *src_relpath, + int delete_op_depth, + const char *dst_relpath, + const svn_skel_t *work_items, + apr_pool_t *scratch_pool); + +svn_error_t * +svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + svn_wc__db_t *db, + svn_boolean_t resolved_text, + svn_boolean_t resolved_props, + svn_boolean_t resolved_tree, + const svn_skel_t *work_items, + apr_pool_t *scratch_pool); + +/* op_depth is the depth at which the node is added. */ +svn_error_t * +svn_wc__db_op_raise_moved_away_internal( + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + svn_wc__db_t *db, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + const svn_wc_conflict_version_t *old_version, + const svn_wc_conflict_version_t *new_version, + apr_pool_t *scratch_pool); svn_error_t * svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, @@ -470,4 +533,10 @@ svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, void *notify_baton, apr_pool_t *scratch_pool); +svn_error_t * +svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot, + svn_wc__db_verify_cb_t callback, + void *baton, + apr_pool_t *scratch_pool); + #endif /* WC_DB_PRIVATE_H */ diff --git a/subversion/libsvn_wc/wc_db_update_move.c b/subversion/libsvn_wc/wc_db_update_move.c index 7f4f853..46cbeae 100644 --- a/subversion/libsvn_wc/wc_db_update_move.c +++ b/subversion/libsvn_wc/wc_db_update_move.c @@ -46,7 +46,7 @@ * layer. The destination may have additional higher op-depths * representing adds, deletes, moves within the move destination. [2] * - * After the intial move an update has modified the NODES in the move + * After the initial move an update has modified the NODES in the move * source and may have introduced a tree-conflict since the source and * destination trees are no longer equivalent. The source is a * different revision and may have text, property and tree changes @@ -88,9 +88,9 @@ #include "svn_sorts.h" #include "private/svn_skel.h" +#include "private/svn_sorts_private.h" #include "private/svn_sqlite.h" #include "private/svn_wc_private.h" -#include "private/svn_editor.h" #include "wc.h" #include "props.h" @@ -100,6 +100,80 @@ #include "workqueue.h" #include "token-map.h" +/* Helper functions */ +/* Return the absolute path, in local path style, of LOCAL_RELPATH + in WCROOT. */ +static const char * +path_for_error_message(const svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *result_pool) +{ + const char *local_abspath + = svn_dirent_join(wcroot->abspath, local_relpath, result_pool); + + return svn_dirent_local_style(local_abspath, result_pool); +} + +/* Ensure that there is a working copy lock for LOCAL_RELPATH in WCROOT */ +static svn_error_t * +verify_write_lock(svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + apr_pool_t *scratch_pool) +{ + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, local_relpath, + FALSE, scratch_pool)); + if (!locked) + { + return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, + _("No write-lock in '%s'"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* In our merge conflicts we record the move_op_src path, which is essentially + the depth at which what was moved is marked deleted. The problem is that + this depth is not guaranteed to be stable, because somebody might just + remove another ancestor, or revert one. + + To work around this problem we locate the layer below this path, and use + that to pinpoint whatever is moved. + + For a path SRC_RELPATH that was deleted by an operation rooted at + DELETE_OP_DEPTH find the op-depth at which the node was originally added. + */ +static svn_error_t * +find_src_op_depth(int *src_op_depth, + svn_wc__db_wcroot_t *wcroot, + const char *src_relpath, + int delete_op_depth, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_HIGHEST_WORKING_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + src_relpath, delete_op_depth)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) + *src_op_depth = svn_sqlite__column_int(stmt, 0); + SVN_ERR(svn_sqlite__reset(stmt)); + if (!have_row) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("'%s' is not deleted"), + path_for_error_message(wcroot, src_relpath, + scratch_pool)); + + return SVN_NO_ERROR; +} + /* * Receiver code. * @@ -113,20 +187,34 @@ * be made at the move destination. */ -struct tc_editor_baton { +typedef struct update_move_baton_t { svn_wc__db_t *db; svn_wc__db_wcroot_t *wcroot; - const char *move_root_dst_relpath; - /* The most recent conflict raised during this drive. We rely on the - non-Ev2, depth-first, drive for this to make sense. */ - const char *conflict_root_relpath; + int src_op_depth; + int dst_op_depth; svn_wc_operation_t operation; svn_wc_conflict_version_t *old_version; svn_wc_conflict_version_t *new_version; - apr_pool_t *result_pool; /* For things that live as long as the baton. */ -}; + + svn_cancel_func_t cancel_func; + void *cancel_baton; +} update_move_baton_t; + +/* Per node flags for tree conflict collection */ +typedef struct node_move_baton_t +{ + svn_boolean_t skip; + svn_boolean_t shadowed; + svn_boolean_t edited; + + const char *src_relpath; + const char *dst_relpath; + + update_move_baton_t *umb; + struct node_move_baton_t *pb; +} node_move_baton_t; /* * Notifications are delayed until the entire update-move transaction @@ -135,24 +223,52 @@ struct tc_editor_baton { * and spooling notifications out of that table after the transaction. */ -/* Add an entry to the notification list. */ +/* Add an entry to the notification list, and at the same time install + a conflict and/or work items. */ static svn_error_t * update_move_list_add(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, + svn_wc__db_t *db, svn_wc_notify_action_t action, svn_node_kind_t kind, svn_wc_notify_state_t content_state, - svn_wc_notify_state_t prop_state) - + svn_wc_notify_state_t prop_state, + svn_skel_t *conflict, + svn_skel_t *work_item, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; + if (conflict) + { + svn_boolean_t tree_conflict; + + SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL, + &tree_conflict, + db, wcroot->abspath, conflict, + scratch_pool, scratch_pool)); + if (tree_conflict) + { + action = svn_wc_notify_tree_conflict; + content_state = svn_wc_notify_state_inapplicable; + prop_state = svn_wc_notify_state_inapplicable; + } + } + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_UPDATE_MOVE_LIST)); - SVN_ERR(svn_sqlite__bindf(stmt, "sdddd", local_relpath, - action, kind, content_state, prop_state)); + SVN_ERR(svn_sqlite__bindf(stmt, "sdtdd", local_relpath, + action, kind_map_none, kind, + content_state, prop_state)); SVN_ERR(svn_sqlite__step_done(stmt)); + if (conflict) + SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, conflict, + scratch_pool)); + + if (work_item) + SVN_ERR(svn_wc__db_wq_add_internal(wcroot, work_item, scratch_pool)); + return SVN_NO_ERROR; } @@ -192,7 +308,7 @@ svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, local_relpath, iterpool), action, iterpool); - notify->kind = svn_sqlite__column_int(stmt, 2); + notify->kind = svn_sqlite__column_token(stmt, 2, kind_map_none); notify->content_state = svn_sqlite__column_int(stmt, 3); notify->prop_state = svn_sqlite__column_int(stmt, 4); notify->old_revision = old_revision; @@ -212,23 +328,25 @@ svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, return SVN_NO_ERROR; } -/* Mark a tree-conflict on LOCAL_RELPATH if such a tree-conflict does - not already exist. */ +/* Create a tree-conflict for recording on LOCAL_RELPATH if such + a tree-conflict does not already exist. */ static svn_error_t * -mark_tree_conflict(const char *local_relpath, - svn_wc__db_wcroot_t *wcroot, - svn_wc__db_t *db, - const svn_wc_conflict_version_t *old_version, - const svn_wc_conflict_version_t *new_version, - const char *move_root_dst_relpath, - svn_wc_operation_t operation, - svn_node_kind_t old_kind, - svn_node_kind_t new_kind, - const char *old_repos_relpath, - svn_wc_conflict_reason_t reason, - svn_wc_conflict_action_t action, - const char *move_src_op_root_relpath, - apr_pool_t *scratch_pool) +create_tree_conflict(svn_skel_t **conflict_p, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + const char *dst_op_root_relpath, + svn_wc__db_t *db, + const svn_wc_conflict_version_t *old_version, + const svn_wc_conflict_version_t *new_version, + svn_wc_operation_t operation, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + const char *old_repos_relpath, + svn_wc_conflict_reason_t reason, + svn_wc_conflict_action_t action, + const char *move_src_op_root_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_error_t *err; svn_skel_t *conflict; @@ -250,14 +368,18 @@ mark_tree_conflict(const char *local_relpath, : NULL; if (!new_repos_relpath) - new_repos_relpath - = svn_relpath_join(new_version->path_in_repos, - svn_relpath_skip_ancestor(move_root_dst_relpath, - local_relpath), - scratch_pool); - - err = svn_wc__db_read_conflict_internal(&conflict, wcroot, local_relpath, - scratch_pool, scratch_pool); + { + const char *child_relpath = svn_relpath_skip_ancestor( + dst_op_root_relpath, + local_relpath); + SVN_ERR_ASSERT(child_relpath != NULL); + new_repos_relpath = svn_relpath_join(new_version->path_in_repos, + child_relpath, scratch_pool); + } + + err = svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, local_relpath, + result_pool, scratch_pool); if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); else if (err) @@ -280,7 +402,7 @@ mark_tree_conflict(const char *local_relpath, && conflict_operation != svn_wc_operation_switch) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("'%s' already in conflict"), - svn_dirent_local_style(local_relpath, + path_for_error_message(wcroot, local_relpath, scratch_pool)); if (tree_conflicted) @@ -302,17 +424,19 @@ mark_tree_conflict(const char *local_relpath, && strcmp(move_src_op_root_relpath, svn_dirent_skip_ancestor(wcroot->abspath, existing_abspath)))) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("'%s' already in conflict"), - svn_dirent_local_style(local_relpath, + path_for_error_message(wcroot, + local_relpath, scratch_pool)); /* Already a suitable tree-conflict. */ + *conflict_p = conflict; return SVN_NO_ERROR; } } else - conflict = svn_wc__conflict_skel_create(scratch_pool); + conflict = svn_wc__conflict_skel_create(result_pool); SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( conflict, db, @@ -321,19 +445,13 @@ mark_tree_conflict(const char *local_relpath, reason, action, move_src_op_root_abspath, - scratch_pool, + result_pool, scratch_pool)); - if (reason != svn_wc_conflict_reason_unversioned - && old_repos_relpath != NULL /* no local additions */) - { - conflict_old_version = svn_wc_conflict_version_create2( + conflict_old_version = svn_wc_conflict_version_create2( old_version->repos_url, old_version->repos_uuid, old_repos_relpath, old_version->peg_rev, old_kind, scratch_pool); - } - else - conflict_old_version = NULL; conflict_new_version = svn_wc_conflict_version_create2( new_version->repos_url, new_version->repos_uuid, @@ -344,312 +462,368 @@ mark_tree_conflict(const char *local_relpath, { SVN_ERR(svn_wc__conflict_skel_set_op_update( conflict, conflict_old_version, conflict_new_version, - scratch_pool, scratch_pool)); + result_pool, scratch_pool)); } else { assert(operation == svn_wc_operation_switch); SVN_ERR(svn_wc__conflict_skel_set_op_switch( conflict, conflict_old_version, conflict_new_version, - scratch_pool, scratch_pool)); + result_pool, scratch_pool)); } - SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, - conflict, scratch_pool)); - - SVN_ERR(update_move_list_add(wcroot, local_relpath, - svn_wc_notify_tree_conflict, - new_kind, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + *conflict_p = conflict; return SVN_NO_ERROR; } -/* If LOCAL_RELPATH is a child of the most recently raised - tree-conflict or is shadowed then set *IS_CONFLICTED to TRUE and - raise a tree-conflict on the root of the obstruction if such a - tree-conflict does not already exist. KIND is the kind of the - incoming LOCAL_RELPATH. This relies on the non-Ev2, depth-first, - drive. */ static svn_error_t * -check_tree_conflict(svn_boolean_t *is_conflicted, - struct tc_editor_baton *b, +create_node_tree_conflict(svn_skel_t **conflict_p, + node_move_baton_t *nmb, + const char *dst_local_relpath, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + svn_wc_conflict_reason_t reason, + svn_wc_conflict_action_t action, + const char *move_src_op_root_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + update_move_baton_t *umb = nmb->umb; + const char *dst_repos_relpath; + const char *dst_root_relpath = svn_relpath_prefix(nmb->dst_relpath, + nmb->umb->dst_op_depth, + scratch_pool); + + dst_repos_relpath = + svn_relpath_join(nmb->umb->old_version->path_in_repos, + svn_relpath_skip_ancestor(dst_root_relpath, + nmb->dst_relpath), + scratch_pool); + + + + return svn_error_trace( + create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath, + svn_relpath_prefix(dst_local_relpath, + umb->dst_op_depth, + scratch_pool), + umb->db, + umb->old_version, umb->new_version, + umb->operation, old_kind, new_kind, + dst_repos_relpath, + reason, action, move_src_op_root_relpath, + result_pool, scratch_pool)); +} + +/* Checks if a specific local path is shadowed as seen from the move root. + Helper for update_moved_away_node() */ +static svn_error_t * +check_node_shadowed(svn_boolean_t *shadowed, + svn_wc__db_wcroot_t *wcroot, const char *local_relpath, - svn_node_kind_t old_kind, - svn_node_kind_t new_kind, - const char *old_repos_relpath, - svn_wc_conflict_action_t action, + int move_root_dst_op_depth, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - int dst_op_depth = relpath_depth(b->move_root_dst_relpath); - int op_depth; - const char *conflict_root_relpath = local_relpath; - const char *move_dst_relpath, *dummy1; - const char *dummy2, *move_src_op_root_relpath; - if (b->conflict_root_relpath) - { - if (svn_relpath_skip_ancestor(b->conflict_root_relpath, local_relpath)) - { - *is_conflicted = TRUE; - return SVN_NO_ERROR; - } - b->conflict_root_relpath = NULL; - } + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_WORKING_NODE)); + SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_LOWEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, local_relpath, - dst_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (have_row) - op_depth = svn_sqlite__column_int(stmt, 0); + { + int op_depth = svn_sqlite__column_int(stmt, 0); + + *shadowed = (op_depth > move_root_dst_op_depth); + } + else + *shadowed = FALSE; SVN_ERR(svn_sqlite__reset(stmt)); - if (!have_row) + return SVN_NO_ERROR; +} + +/* Set a tree conflict for the shadowed node LOCAL_RELPATH, which is + the ROOT OF THE OBSTRUCTION if such a tree-conflict does not + already exist. KIND is the kind of the incoming LOCAL_RELPATH. */ +static svn_error_t * +mark_tc_on_op_root(node_move_baton_t *nmb, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, + svn_wc_conflict_action_t action, + apr_pool_t *scratch_pool) +{ + update_move_baton_t *b = nmb->umb; + const char *move_dst_relpath; + svn_skel_t *conflict; + + SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed); + + nmb->skip = TRUE; + + if (old_kind == svn_node_none) + move_dst_relpath = NULL; + else + SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL, + b->wcroot, nmb->dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath, + old_kind, new_kind, + (move_dst_relpath + ? svn_wc_conflict_reason_moved_away + : svn_wc_conflict_reason_deleted), + action, move_dst_relpath + ? nmb->dst_relpath + : NULL, + scratch_pool, scratch_pool)); + + SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db, + svn_wc_notify_tree_conflict, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +mark_node_edited(node_move_baton_t *nmb, + apr_pool_t *scratch_pool) +{ + if (nmb->edited) + return SVN_NO_ERROR; + + if (nmb->pb) { - *is_conflicted = FALSE; - return SVN_NO_ERROR; + SVN_ERR(mark_node_edited(nmb->pb, scratch_pool)); + + if (nmb->pb->skip) + nmb->skip = TRUE; } - *is_conflicted = TRUE; + nmb->edited = TRUE; - while (relpath_depth(conflict_root_relpath) > op_depth) + if (nmb->skip) + return SVN_NO_ERROR; + + if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed)) { - conflict_root_relpath = svn_relpath_dirname(conflict_root_relpath, - scratch_pool); - old_kind = new_kind = svn_node_dir; - if (old_repos_relpath) - old_repos_relpath = svn_relpath_dirname(old_repos_relpath, - scratch_pool); - action = svn_wc_conflict_action_edit; + svn_node_kind_t dst_kind, src_kind; + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &dst_kind, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + nmb->umb->wcroot, nmb->dst_relpath, + nmb->umb->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + nmb->umb->wcroot, nmb->src_relpath, + nmb->umb->src_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(mark_tc_on_op_root(nmb, + dst_kind, src_kind, + svn_wc_conflict_action_edit, + scratch_pool)); } - SVN_ERR(svn_wc__db_op_depth_moved_to(&move_dst_relpath, - &dummy1, - &dummy2, - &move_src_op_root_relpath, - dst_op_depth, - b->wcroot, conflict_root_relpath, - scratch_pool, scratch_pool)); - - SVN_ERR(mark_tree_conflict(conflict_root_relpath, - b->wcroot, b->db, b->old_version, b->new_version, - b->move_root_dst_relpath, b->operation, - old_kind, new_kind, - old_repos_relpath, - (move_dst_relpath - ? svn_wc_conflict_reason_moved_away - : svn_wc_conflict_reason_deleted), - action, move_src_op_root_relpath, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, conflict_root_relpath); + return SVN_NO_ERROR; +} + +static svn_error_t * +mark_parent_edited(node_move_baton_t *nmb, + apr_pool_t *scratch_pool) +{ + SVN_ERR_ASSERT(nmb && nmb->pb); + + SVN_ERR(mark_node_edited(nmb->pb, scratch_pool)); + + if (nmb->pb->skip) + nmb->skip = TRUE; return SVN_NO_ERROR; } static svn_error_t * -tc_editor_add_directory(void *baton, +tc_editor_add_directory(node_move_baton_t *nmb, const char *relpath, - const apr_array_header_t *children, + svn_node_kind_t old_kind, apr_hash_t *props, - svn_revnum_t replaces_rev, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_boolean_t is_conflicted; - const char *abspath; - svn_node_kind_t old_kind; - svn_skel_t *work_item; - svn_wc_notify_action_t action = svn_wc_notify_update_add; - svn_error_t *err; + update_move_baton_t *b = nmb->umb; + const char *local_abspath; + svn_node_kind_t wc_kind; + svn_skel_t *work_item = NULL; + svn_skel_t *conflict = NULL; + svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned; + + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; - /* Update NODES, only the bits not covered by the later call to - replace_moved_layer. */ - SVN_ERR(svn_wc__db_extend_parent_delete(b->wcroot, relpath, svn_node_dir, - op_depth, scratch_pool)); - - err = svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + if (nmb->shadowed) { - svn_error_clear(err); - old_kind = svn_node_none; - move_dst_repos_relpath = NULL; + svn_wc__db_status_t status; + + SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + b->wcroot, relpath, + scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_deleted) + reason = svn_wc_conflict_reason_deleted; + else if (status != svn_wc__db_status_added) + wc_kind = svn_node_none; + else if (old_kind == svn_node_none) + reason = svn_wc_conflict_reason_added; + else + reason = svn_wc_conflict_reason_replaced; } else + wc_kind = svn_node_none; + + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); + + if (wc_kind == svn_node_none) { - SVN_ERR(err); - old_kind = move_dst_kind; + /* Check for unversioned tree-conflict */ + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); } - /* Check for NODES tree-conflict. */ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - old_kind, svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_action_add, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; + if (!nmb->shadowed && wc_kind == old_kind) + wc_kind = svn_node_none; /* Node will be gone once we install */ - /* Check for unversioned tree-conflict */ - abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - SVN_ERR(svn_io_check_path(abspath, &old_kind, scratch_pool)); - - switch (old_kind) + if (wc_kind != svn_node_none + && (nmb->shadowed || wc_kind != old_kind)) /* replace */ { - case svn_node_file: - default: - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, old_kind, svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_reason_unversioned, - svn_wc_conflict_action_add, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - action = svn_wc_notify_tree_conflict; - is_conflicted = TRUE; - break; - - case svn_node_none: - SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, abspath, + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, svn_node_dir, + reason, + (old_kind == svn_node_none) + ? svn_wc_conflict_action_add + : svn_wc_conflict_action_replace, + NULL, + scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else + { + SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath, scratch_pool, scratch_pool)); - - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - scratch_pool)); - /* Fall through */ - case svn_node_dir: - break; } - if (!is_conflicted) - SVN_ERR(update_move_list_add(b->wcroot, relpath, - action, - svn_node_dir, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + (old_kind == svn_node_none) + ? svn_wc_notify_update_add + : svn_wc_notify_update_replace, + svn_node_dir, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, work_item, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * -tc_editor_add_file(void *baton, +tc_editor_add_file(node_move_baton_t *nmb, const char *relpath, + svn_node_kind_t old_kind, const svn_checksum_t *checksum, - svn_stream_t *contents, apr_hash_t *props, - svn_revnum_t replaces_rev, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_node_kind_t old_kind; - svn_boolean_t is_conflicted; - const char *abspath; - svn_skel_t *work_item; - svn_error_t *err; + update_move_baton_t *b = nmb->umb; + svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned; + svn_node_kind_t wc_kind; + const char *local_abspath; + svn_skel_t *work_item = NULL; + svn_skel_t *conflict = NULL; + + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; - /* Update NODES, only the bits not covered by the later call to - replace_moved_layer. */ - SVN_ERR(svn_wc__db_extend_parent_delete(b->wcroot, relpath, svn_node_file, - op_depth, scratch_pool)); - - err = svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + if (nmb->shadowed) { - svn_error_clear(err); - old_kind = svn_node_none; - move_dst_repos_relpath = NULL; + svn_wc__db_status_t status; + + SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + b->wcroot, relpath, + scratch_pool, scratch_pool)); + + if (status == svn_wc__db_status_deleted) + reason = svn_wc_conflict_reason_deleted; + else if (status != svn_wc__db_status_added) + wc_kind = svn_node_none; + else if (old_kind == svn_node_none) + reason = svn_wc_conflict_reason_added; + else + reason = svn_wc_conflict_reason_replaced; } else - { - SVN_ERR(err); - old_kind = move_dst_kind; - } - - /* Check for NODES tree-conflict. */ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - old_kind, svn_node_file, move_dst_repos_relpath, - svn_wc_conflict_action_add, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; + wc_kind = svn_node_none; - /* Check for unversioned tree-conflict */ - abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - SVN_ERR(svn_io_check_path(abspath, &old_kind, scratch_pool)); + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); - if (old_kind != svn_node_none) + if (wc_kind == svn_node_none) { - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, old_kind, svn_node_file, - move_dst_repos_relpath, - svn_wc_conflict_reason_unversioned, - svn_wc_conflict_action_add, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - return SVN_NO_ERROR; + /* Check for unversioned tree-conflict */ + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); } - /* Update working file. */ - SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, - svn_dirent_join(b->wcroot->abspath, - relpath, - scratch_pool), + if (wc_kind != svn_node_none + && (nmb->shadowed || wc_kind != old_kind)) /* replace */ + { + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, svn_node_file, + reason, + (old_kind == svn_node_none) + ? svn_wc_conflict_action_add + : svn_wc_conflict_action_replace, NULL, - FALSE /* FIXME: use_commit_times? */, - TRUE /* record_file_info */, scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else + { + /* Update working file. */ + SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, + svn_dirent_join(b->wcroot->abspath, + relpath, + scratch_pool), + NULL, + FALSE /*FIXME: use_commit_times?*/, + TRUE /* record_file_info */, + scratch_pool, scratch_pool)); + } - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - scratch_pool)); - - SVN_ERR(update_move_list_add(b->wcroot, relpath, - svn_wc_notify_update_add, + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + (old_kind == svn_node_none) + ? svn_wc_notify_update_add + : svn_wc_notify_update_replace, svn_node_file, svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); + svn_wc_notify_state_inapplicable, + conflict, work_item, scratch_pool)); return SVN_NO_ERROR; } -static svn_error_t * -tc_editor_add_symlink(void *baton, - const char *relpath, - const char *target, - apr_hash_t *props, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_add_absent(void *baton, - const char *relpath, - svn_node_kind_t kind, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - /* All the info we need about one version of a working node. */ typedef struct working_node_version_t { @@ -669,6 +843,7 @@ create_conflict_markers(svn_skel_t **work_items, const working_node_version_t *old_version, const working_node_version_t *new_version, svn_node_kind_t kind, + svn_boolean_t set_operation, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -689,19 +864,22 @@ create_conflict_markers(svn_skel_t **work_items, = svn_relpath_join(conflicted_version->path_in_repos, part, scratch_pool); original_version->path_in_repos = repos_relpath; - if (operation == svn_wc_operation_update) + if (set_operation) { - SVN_ERR(svn_wc__conflict_skel_set_op_update( - conflict_skel, original_version, - conflicted_version, - scratch_pool, scratch_pool)); - } - else - { - SVN_ERR(svn_wc__conflict_skel_set_op_switch( - conflict_skel, original_version, - conflicted_version, - scratch_pool, scratch_pool)); + if (operation == svn_wc_operation_update) + { + SVN_ERR(svn_wc__conflict_skel_set_op_update( + conflict_skel, original_version, + conflicted_version, + scratch_pool, scratch_pool)); + } + else + { + SVN_ERR(svn_wc__conflict_skel_set_op_switch( + conflict_skel, original_version, + conflicted_version, + scratch_pool, scratch_pool)); + } } /* According to this func's doc string, it is "Currently only used for @@ -720,8 +898,8 @@ update_working_props(svn_wc_notify_state_t *prop_state, svn_skel_t **conflict_skel, apr_array_header_t **propchanges, apr_hash_t **actual_props, - svn_wc__db_t *db, - const char *local_abspath, + update_move_baton_t *b, + const char *local_relpath, const struct working_node_version_t *old_version, const struct working_node_version_t *new_version, apr_pool_t *result_pool, @@ -736,139 +914,123 @@ update_working_props(svn_wc_notify_state_t *prop_state, * merge-left version, and the current props of the * moved-here working file as the merge-right version. */ - SVN_ERR(svn_wc__db_read_props(actual_props, - db, local_abspath, - result_pool, scratch_pool)); + SVN_ERR(svn_wc__db_read_props_internal(actual_props, + b->wcroot, local_relpath, + result_pool, scratch_pool)); SVN_ERR(svn_prop_diffs(propchanges, new_version->props, old_version->props, result_pool)); SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state, &new_actual_props, - db, local_abspath, + b->db, svn_dirent_join(b->wcroot->abspath, + local_relpath, + scratch_pool), old_version->props, old_version->props, *actual_props, *propchanges, result_pool, scratch_pool)); - /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props - relies on NODES row having been updated first which we don't do - at present. So this extra property diff has the same effect. + /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props_internal + relies on NODES row being updated via a different route . - ### Perhaps we should update NODES first (but after - ### svn_wc__db_read_props above)? */ + This extra property diff makes sure we clear the actual row when + the final result is unchanged properties. */ SVN_ERR(svn_prop_diffs(&new_propchanges, new_actual_props, new_version->props, scratch_pool)); if (!new_propchanges->nelts) new_actual_props = NULL; - /* Install the new actual props. Don't set the conflict_skel yet, because - we might need to add a text conflict to it as well. */ - SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, - new_actual_props, - svn_wc__has_magic_property(*propchanges), - NULL/*conflict_skel*/, NULL/*work_items*/, - scratch_pool)); + /* Install the new actual props. */ + SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath, + new_actual_props, + svn_wc__has_magic_property( + *propchanges), + scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * -tc_editor_alter_directory(void *baton, +tc_editor_alter_directory(node_move_baton_t *nmb, const char *dst_relpath, - svn_revnum_t expected_move_dst_revision, - const apr_array_header_t *children, + apr_hash_t *old_props, apr_hash_t *new_props, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; - const char *move_dst_repos_relpath; - svn_revnum_t move_dst_revision; - svn_node_kind_t move_dst_kind; + update_move_baton_t *b = nmb->umb; working_node_version_t old_version, new_version; - svn_wc__db_status_t status; - svn_boolean_t is_conflicted; - - SVN_ERR_ASSERT(expected_move_dst_revision == b->old_version->peg_rev); - - SVN_ERR(svn_wc__db_depth_get_info(&status, &move_dst_kind, &move_dst_revision, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, &old_version.checksum, NULL, - NULL, &old_version.props, - b->wcroot, dst_relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); + svn_skel_t *work_items = NULL; + svn_skel_t *conflict_skel = NULL; + const char *local_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, + scratch_pool); + svn_wc_notify_state_t prop_state; + apr_hash_t *actual_props; + apr_array_header_t *propchanges; + svn_node_kind_t wc_kind; + svn_boolean_t obstructed = FALSE; - /* If the node would be recorded as svn_wc__db_status_base_deleted it - wouldn't have a repos_relpath */ - /* ### Can svn_wc__db_depth_get_info() do this for us without this hint? */ - if (status == svn_wc__db_status_deleted && move_dst_repos_relpath) - status = svn_wc__db_status_not_present; - - /* There might be not-present nodes of a different revision as the same - depth as a copy. This is commonly caused by copying/moving mixed revision - directories */ - SVN_ERR_ASSERT(move_dst_revision == expected_move_dst_revision - || status == svn_wc__db_status_not_present); - SVN_ERR_ASSERT(move_dst_kind == svn_node_dir); - - SVN_ERR(check_tree_conflict(&is_conflicted, b, dst_relpath, - move_dst_kind, - svn_node_dir, - move_dst_repos_relpath, - svn_wc_conflict_action_edit, - scratch_pool)); - if (is_conflicted) + SVN_ERR(mark_node_edited(nmb, scratch_pool)); + if (nmb->skip) return SVN_NO_ERROR; + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); + if (wc_kind != svn_node_none && wc_kind != svn_node_dir) + { + SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, + svn_node_dir, svn_node_dir, + svn_wc_conflict_reason_obstructed, + svn_wc_conflict_action_edit, + NULL, + scratch_pool, scratch_pool)); + obstructed = TRUE; + } + old_version.location_and_kind = b->old_version; new_version.location_and_kind = b->new_version; + old_version.checksum = NULL; /* not a file */ + old_version.props = old_props; new_version.checksum = NULL; /* not a file */ - new_version.props = new_props ? new_props : old_version.props; + new_version.props = new_props; - if (new_props) + SVN_ERR(update_working_props(&prop_state, &conflict_skel, + &propchanges, &actual_props, + b, dst_relpath, + &old_version, &new_version, + scratch_pool, scratch_pool)); + + if (prop_state == svn_wc_notify_state_conflicted) { - const char *dst_abspath = svn_dirent_join(b->wcroot->abspath, - dst_relpath, - scratch_pool); - svn_wc_notify_state_t prop_state; - svn_skel_t *conflict_skel = NULL; - apr_hash_t *actual_props; - apr_array_header_t *propchanges; - - SVN_ERR(update_working_props(&prop_state, &conflict_skel, - &propchanges, &actual_props, - b->db, dst_abspath, - &old_version, &new_version, - scratch_pool, scratch_pool)); - - if (conflict_skel) - { - svn_skel_t *work_items; - - SVN_ERR(create_conflict_markers(&work_items, dst_abspath, - b->db, move_dst_repos_relpath, - conflict_skel, b->operation, - &old_version, &new_version, - svn_node_dir, - scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__db_mark_conflict_internal(b->wcroot, dst_relpath, - conflict_skel, - scratch_pool)); - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_items, - scratch_pool)); - } + const char *move_dst_repos_relpath; - SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, - svn_wc_notify_update_update, - svn_node_dir, - svn_wc_notify_state_inapplicable, - prop_state)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, + &move_dst_repos_relpath, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + b->wcroot, dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_conflict_markers(&work_items, local_abspath, + b->db, move_dst_repos_relpath, + conflict_skel, b->operation, + &old_version, &new_version, + svn_node_dir, !obstructed, + scratch_pool, scratch_pool)); } + SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, + svn_wc_notify_update_update, + svn_node_dir, + svn_wc_notify_state_inapplicable, + prop_state, + conflict_skel, work_items, scratch_pool)); + return SVN_NO_ERROR; } - -/* Merge the difference between OLD_VERSION and NEW_VERSION into +/* Edit the file found at the move destination, which is initially at + * the old state. Merge the changes into the "working"/"actual" file. + * + * Merge the difference between OLD_VERSION and NEW_VERSION into * the working file at LOCAL_RELPATH. * * The term 'old' refers to the pre-update state, which is the state of @@ -883,17 +1045,18 @@ tc_editor_alter_directory(void *baton, * Set *WORK_ITEMS to any required work items, allocated in RESULT_POOL. * Use SCRATCH_POOL for temporary allocations. */ static svn_error_t * -update_working_file(const char *local_relpath, - const char *repos_relpath, - svn_wc_operation_t operation, - const working_node_version_t *old_version, - const working_node_version_t *new_version, - svn_wc__db_wcroot_t *wcroot, - svn_wc__db_t *db, - apr_pool_t *scratch_pool) +tc_editor_alter_file(node_move_baton_t *nmb, + const char *dst_relpath, + const svn_checksum_t *old_checksum, + const svn_checksum_t *new_checksum, + apr_hash_t *old_props, + apr_hash_t *new_props, + apr_pool_t *scratch_pool) { - const char *local_abspath = svn_dirent_join(wcroot->abspath, - local_relpath, + update_move_baton_t *b = nmb->umb; + working_node_version_t old_version, new_version; + const char *local_abspath = svn_dirent_join(b->wcroot->abspath, + dst_relpath, scratch_pool); const char *old_pristine_abspath; const char *new_pristine_abspath; @@ -903,23 +1066,51 @@ update_working_file(const char *local_relpath, enum svn_wc_merge_outcome_t merge_outcome; svn_wc_notify_state_t prop_state, content_state; svn_skel_t *work_item, *work_items = NULL; + svn_node_kind_t wc_kind; + svn_boolean_t obstructed = FALSE; + SVN_ERR(mark_node_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; + + SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); + if (wc_kind != svn_node_none && wc_kind != svn_node_file) + { + SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, + svn_node_file, svn_node_file, + svn_wc_conflict_reason_obstructed, + svn_wc_conflict_action_edit, + NULL, + scratch_pool, scratch_pool)); + obstructed = TRUE; + } + + old_version.location_and_kind = b->old_version; + new_version.location_and_kind = b->new_version; + + old_version.checksum = old_checksum; + old_version.props = old_props; + new_version.checksum = new_checksum; + new_version.props = new_props; + + /* ### TODO: Only do this when there is no higher WORKING layer */ SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges, - &actual_props, db, local_abspath, - old_version, new_version, + &actual_props, b, dst_relpath, + &old_version, &new_version, scratch_pool, scratch_pool)); - if (!svn_checksum_match(new_version->checksum, old_version->checksum)) + if (!obstructed + && !svn_checksum_match(new_version.checksum, old_version.checksum)) { svn_boolean_t is_locally_modified; SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified, - db, local_abspath, + b->db, local_abspath, FALSE /* exact_comparison */, scratch_pool)); if (!is_locally_modified) { - SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, + SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, local_abspath, NULL, FALSE /* FIXME: use_commit_times? */, @@ -939,15 +1130,15 @@ update_working_file(const char *local_relpath, * moved-here working file as the merge-right version. */ SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath, - db, wcroot->abspath, - old_version->checksum, + b->db, b->wcroot->abspath, + old_version.checksum, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, - db, wcroot->abspath, - new_version->checksum, + b->db, b->wcroot->abspath, + new_version.checksum, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel, - &merge_outcome, db, + &merge_outcome, b->db, old_pristine_abspath, new_pristine_abspath, local_abspath, @@ -958,7 +1149,7 @@ update_working_file(const char *local_relpath, NULL, /* diff3-cmd */ NULL, /* merge options */ propchanges, - NULL, NULL, /* cancel_func + baton */ + b->cancel_func, b->cancel_baton, scratch_pool, scratch_pool)); work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); @@ -976,207 +1167,113 @@ update_working_file(const char *local_relpath, * too. */ if (conflict_skel) { - SVN_ERR(create_conflict_markers(&work_item, local_abspath, db, - repos_relpath, conflict_skel, - operation, old_version, new_version, - svn_node_file, - scratch_pool, scratch_pool)); + const char *move_dst_repos_relpath; - SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, - conflict_skel, - scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, + &move_dst_repos_relpath, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + b->wcroot, dst_relpath, + b->dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(create_conflict_markers(&work_item, local_abspath, b->db, + move_dst_repos_relpath, conflict_skel, + b->operation, &old_version, &new_version, + svn_node_file, !obstructed, + scratch_pool, scratch_pool)); work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); } - SVN_ERR(svn_wc__db_wq_add(db, wcroot->abspath, work_items, scratch_pool)); - - SVN_ERR(update_move_list_add(wcroot, local_relpath, + SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, svn_wc_notify_update_update, svn_node_file, content_state, - prop_state)); + prop_state, + conflict_skel, work_items, scratch_pool)); return SVN_NO_ERROR; } - -/* Edit the file found at the move destination, which is initially at - * the old state. Merge the changes into the "working"/"actual" file. - */ static svn_error_t * -tc_editor_alter_file(void *baton, - const char *dst_relpath, - svn_revnum_t expected_move_dst_revision, - apr_hash_t *new_props, - const svn_checksum_t *new_checksum, - svn_stream_t *new_contents, - apr_pool_t *scratch_pool) -{ - struct tc_editor_baton *b = baton; - const char *move_dst_repos_relpath; - svn_revnum_t move_dst_revision; - svn_node_kind_t move_dst_kind; - working_node_version_t old_version, new_version; - svn_boolean_t is_conflicted; - svn_wc__db_status_t status; - - SVN_ERR(svn_wc__db_depth_get_info(&status, &move_dst_kind, &move_dst_revision, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, &old_version.checksum, NULL, - NULL, &old_version.props, - b->wcroot, dst_relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); - - /* If the node would be recorded as svn_wc__db_status_base_deleted it - wouldn't have a repos_relpath */ - /* ### Can svn_wc__db_depth_get_info() do this for us without this hint? */ - if (status == svn_wc__db_status_deleted && move_dst_repos_relpath) - status = svn_wc__db_status_not_present; - - SVN_ERR_ASSERT(move_dst_revision == expected_move_dst_revision - || status == svn_wc__db_status_not_present); - SVN_ERR_ASSERT(move_dst_kind == svn_node_file); - - SVN_ERR(check_tree_conflict(&is_conflicted, b, dst_relpath, - move_dst_kind, - svn_node_file, - move_dst_repos_relpath, - svn_wc_conflict_action_edit, - scratch_pool)); - if (is_conflicted) - return SVN_NO_ERROR; - - old_version.location_and_kind = b->old_version; - new_version.location_and_kind = b->new_version; - - /* If new checksum is null that means no change; similarly props. */ - new_version.checksum = new_checksum ? new_checksum : old_version.checksum; - new_version.props = new_props ? new_props : old_version.props; - - /* Update file and prop contents if the update has changed them. */ - if (!svn_checksum_match(new_checksum, old_version.checksum) || new_props) - { - SVN_ERR(update_working_file(dst_relpath, move_dst_repos_relpath, - b->operation, &old_version, &new_version, - b->wcroot, b->db, - scratch_pool)); - } - - return SVN_NO_ERROR; -} - -static svn_error_t * -tc_editor_alter_symlink(void *baton, - const char *relpath, - svn_revnum_t revision, - apr_hash_t *props, - const char *target, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_delete(void *baton, +tc_editor_delete(node_move_baton_t *nmb, const char *relpath, - svn_revnum_t revision, + svn_node_kind_t old_kind, + svn_node_kind_t new_kind, apr_pool_t *scratch_pool) { - struct tc_editor_baton *b = baton; + update_move_baton_t *b = nmb->umb; svn_sqlite__stmt_t *stmt; - int op_depth = relpath_depth(b->move_root_dst_relpath); - const char *move_dst_repos_relpath; - svn_node_kind_t move_dst_kind; - svn_boolean_t is_conflicted; - svn_boolean_t must_delete_working_nodes = FALSE; - const char *local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, - scratch_pool); - const char *parent_relpath = svn_relpath_dirname(relpath, scratch_pool); - int op_depth_below; - svn_boolean_t have_row; + const char *local_abspath; + svn_boolean_t is_modified, is_all_deletes; + svn_skel_t *work_items = NULL; + svn_skel_t *conflict = NULL; - SVN_ERR(svn_wc__db_depth_get_info(NULL, &move_dst_kind, NULL, - &move_dst_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - b->wcroot, relpath, - relpath_depth(b->move_root_dst_relpath), - scratch_pool, scratch_pool)); + SVN_ERR(mark_parent_edited(nmb, scratch_pool)); + if (nmb->skip) + return SVN_NO_ERROR; /* Check before retracting delete to catch delete-delete conflicts. This catches conflicts on the node itself; deleted children are caught as local modifications below.*/ - SVN_ERR(check_tree_conflict(&is_conflicted, b, relpath, - move_dst_kind, - svn_node_unknown, - move_dst_repos_relpath, - svn_wc_conflict_action_delete, - scratch_pool)); + if (nmb->shadowed) + { + SVN_ERR(mark_tc_on_op_root(nmb, + old_kind, new_kind, + svn_wc_conflict_action_delete, + scratch_pool)); + return SVN_NO_ERROR; + } - if (!is_conflicted) + local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); + SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes, + nmb->umb->db, local_abspath, FALSE, + NULL, NULL, scratch_pool)); + if (is_modified) { - svn_boolean_t is_modified, is_all_deletes; + svn_wc_conflict_reason_t reason; - SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes, b->db, - local_abspath, - NULL, NULL, scratch_pool)); - if (is_modified) - { - svn_wc_conflict_reason_t reason; + /* No conflict means no NODES rows at the relpath op-depth + so it's easy to convert the modified tree into a copy. - if (!is_all_deletes) - { - /* No conflict means no NODES rows at the relpath op-depth - so it's easy to convert the modified tree into a copy. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_UPDATE_OP_DEPTH_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", b->wcroot->wc_id, relpath, - op_depth, relpath_depth(relpath))); - SVN_ERR(svn_sqlite__step_done(stmt)); - - reason = svn_wc_conflict_reason_edited; - } - else - { + Note the following assumptions for relpath: + * it is not shadowed + * it is not the/an op-root. (or we can't make us a copy) + */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_WORKING_OP_DEPTH_ABOVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); + SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE, + NULL, NULL, scratch_pool)); - reason = svn_wc_conflict_reason_deleted; - must_delete_working_nodes = TRUE; - } - is_conflicted = TRUE; - SVN_ERR(mark_tree_conflict(relpath, b->wcroot, b->db, b->old_version, - b->new_version, b->move_root_dst_relpath, - b->operation, - move_dst_kind, - svn_node_none, - move_dst_repos_relpath, reason, - svn_wc_conflict_action_delete, NULL, - scratch_pool)); - b->conflict_root_relpath = apr_pstrdup(b->result_pool, relpath); - } - } + reason = svn_wc_conflict_reason_edited; - if (!is_conflicted || must_delete_working_nodes) + SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, + old_kind, new_kind, reason, + (new_kind == svn_node_none) + ? svn_wc_conflict_action_delete + : svn_wc_conflict_action_replace, + NULL, + scratch_pool, scratch_pool)); + nmb->skip = TRUE; + } + else { apr_pool_t *iterpool = svn_pool_create(scratch_pool); - svn_skel_t *work_item; - svn_node_kind_t del_kind; const char *del_abspath; + svn_boolean_t have_row; + /* Get all descendants of the node in reverse order (so children are + handled before their parents, but not strictly depth first) */ SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_CHILDREN_OP_DEPTH)); + STMT_SELECT_DESCENDANTS_OP_DEPTH_RV)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); + b->dst_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); while (have_row) { svn_error_t *err; + svn_skel_t *work_item; + svn_node_kind_t del_kind; svn_pool_clear(iterpool); @@ -1194,8 +1291,7 @@ tc_editor_delete(void *baton, b->wcroot->abspath, del_abspath, iterpool, iterpool); if (!err) - err = svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - iterpool); + err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool); if (err) return svn_error_compose_create(err, svn_sqlite__reset(stmt)); @@ -1203,139 +1299,34 @@ tc_editor_delete(void *baton, } SVN_ERR(svn_sqlite__reset(stmt)); - SVN_ERR(svn_wc__db_depth_get_info(NULL, &del_kind, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, - b->wcroot, relpath, op_depth, - iterpool, iterpool)); - if (del_kind == svn_node_dir) - SVN_ERR(svn_wc__wq_build_dir_remove(&work_item, b->db, + if (old_kind == svn_node_dir) + SVN_ERR(svn_wc__wq_build_dir_remove(&work_items, b->db, b->wcroot->abspath, local_abspath, FALSE /* recursive */, - iterpool, iterpool)); + scratch_pool, iterpool)); else - SVN_ERR(svn_wc__wq_build_file_remove(&work_item, b->db, + SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db, b->wcroot->abspath, local_abspath, - iterpool, iterpool)); - SVN_ERR(svn_wc__db_wq_add(b->db, b->wcroot->abspath, work_item, - iterpool)); - - if (!is_conflicted) - SVN_ERR(update_move_list_add(b->wcroot, relpath, - svn_wc_notify_update_delete, - del_kind, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); - svn_pool_destroy(iterpool); - } + scratch_pool, iterpool)); - /* Deleting the ROWS is valid so long as we update the parent before - committing the transaction. The removed rows could have been - replacing a lower layer in which case we need to add base-deleted - rows. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_SELECT_HIGHEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, parent_relpath, - op_depth)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - op_depth_below = svn_sqlite__column_int(stmt, 0); - SVN_ERR(svn_sqlite__reset(stmt)); - if (have_row) - { - /* Remove non-shadowing nodes. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_NO_LOWER_LAYER)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", b->wcroot->wc_id, relpath, - op_depth, op_depth_below)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - /* Convert remaining shadowing nodes to presence='base-deleted'. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_REPLACE_WITH_BASE_DELETED)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - } - else - { - SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, - STMT_DELETE_WORKING_OP_DEPTH)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, - op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); + svn_pool_destroy(iterpool); } - /* Retract any base-delete. */ - SVN_ERR(svn_wc__db_retract_parent_delete(b->wcroot, relpath, op_depth, - scratch_pool)); - - return SVN_NO_ERROR; -} - -static svn_error_t * -tc_editor_copy(void *baton, - const char *src_relpath, - svn_revnum_t src_revision, - const char *dst_relpath, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_move(void *baton, - const char *src_relpath, - svn_revnum_t src_revision, - const char *dst_relpath, - svn_revnum_t replaces_rev, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_rotate(void *baton, - const apr_array_header_t *relpaths, - const apr_array_header_t *revisions, - apr_pool_t *scratch_pool) -{ - return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL); -} - -static svn_error_t * -tc_editor_complete(void *baton, - apr_pool_t *scratch_pool) -{ - return SVN_NO_ERROR; -} + /* Only notify if add_file/add_dir is not going to notify */ + if (conflict || (new_kind == svn_node_none)) + SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, + svn_wc_notify_update_delete, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, work_items, scratch_pool)); + else if (work_items) + SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items, + scratch_pool)); -static svn_error_t * -tc_editor_abort(void *baton, - apr_pool_t *scratch_pool) -{ return SVN_NO_ERROR; } -/* The editor callback table implementing the receiver. */ -static const svn_editor_cb_many_t editor_ops = { - tc_editor_add_directory, - tc_editor_add_file, - tc_editor_add_symlink, - tc_editor_add_absent, - tc_editor_alter_directory, - tc_editor_alter_file, - tc_editor_alter_symlink, - tc_editor_delete, - tc_editor_copy, - tc_editor_move, - tc_editor_rotate, - tc_editor_complete, - tc_editor_abort -}; - - /* * Driver code. * @@ -1352,73 +1343,11 @@ static const svn_editor_cb_many_t editor_ops = { * single-revision. */ -/* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION - * to reflect the tree conflict on the victim SRC_ABSPATH in DB. - * - * If SRC_ABSPATH is not a tree-conflict victim, return an error. - */ -static svn_error_t * -get_tc_info(svn_wc_operation_t *operation, - svn_wc_conflict_reason_t *local_change, - svn_wc_conflict_action_t *incoming_change, - const char **move_src_op_root_abspath, - svn_wc_conflict_version_t **old_version, - svn_wc_conflict_version_t **new_version, - svn_wc__db_t *db, - const char *src_abspath, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const apr_array_header_t *locations; - svn_boolean_t tree_conflicted; - svn_skel_t *conflict_skel; - - /* Check for tree conflict on src. */ - SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db, - src_abspath, - scratch_pool, scratch_pool)); - if (!conflict_skel) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not in conflict"), - svn_dirent_local_style(src_abspath, - scratch_pool)); - - SVN_ERR(svn_wc__conflict_read_info(operation, &locations, - NULL, NULL, &tree_conflicted, - db, src_abspath, - conflict_skel, result_pool, - scratch_pool)); - if ((*operation != svn_wc_operation_update - && *operation != svn_wc_operation_switch) - || !tree_conflicted) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not a tree-conflict victim"), - svn_dirent_local_style(src_abspath, - scratch_pool)); - if (locations) - { - SVN_ERR_ASSERT(locations->nelts >= 2); - *old_version = APR_ARRAY_IDX(locations, 0, - svn_wc_conflict_version_t *); - *new_version = APR_ARRAY_IDX(locations, 1, - svn_wc_conflict_version_t *); - } - - SVN_ERR(svn_wc__conflict_read_tree_conflict(local_change, - incoming_change, - move_src_op_root_abspath, - db, src_abspath, - conflict_skel, scratch_pool, - scratch_pool)); - - return SVN_NO_ERROR; -} - /* Return *PROPS, *CHECKSUM, *CHILDREN and *KIND for LOCAL_RELPATH at OP_DEPTH provided the row exists. Return *KIND of svn_node_none if - the row does not exist. *CHILDREN is a sorted array of basenames of - type 'const char *', rather than a hash, to allow the driver to - process children in a defined order. */ + the row does not exist, or only describes a delete of a lower op-depth. + *CHILDREN is a sorted array of basenames of type 'const char *', rather + than a hash, to allow the driver to process children in a defined order. */ static svn_error_t * get_info(apr_hash_t **props, const svn_checksum_t **checksum, @@ -1430,64 +1359,68 @@ get_info(apr_hash_t **props, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_hash_t *hash_children; - apr_array_header_t *sorted_children; + svn_wc__db_status_t status; + const char *repos_relpath; + svn_node_kind_t db_kind; svn_error_t *err; - int i; - err = svn_wc__db_depth_get_info(NULL, kind, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, checksum, NULL, NULL, props, + err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL, + NULL, NULL, NULL, NULL, checksum, NULL, + NULL, props, wcroot, local_relpath, op_depth, result_pool, scratch_pool); - if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + + /* If there is no node at this depth, or only a node that describes a delete + of a lower layer we report this node as not existing. */ + if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) + || (!err && status != svn_wc__db_status_added + && status != svn_wc__db_status_normal)) { svn_error_clear(err); - *kind = svn_node_none; + + if (kind) + *kind = svn_node_none; + if (checksum) + *checksum = NULL; + if (props) + *props = NULL; + if (children) + *children = apr_array_make(result_pool, 0, sizeof(const char *)); + + return SVN_NO_ERROR; } else SVN_ERR(err); + if (kind) + *kind = db_kind; - SVN_ERR(svn_wc__db_get_children_op_depth(&hash_children, wcroot, - local_relpath, op_depth, - scratch_pool, scratch_pool)); - - sorted_children = svn_sort__hash(hash_children, - svn_sort_compare_items_lexically, - scratch_pool); - - *children = apr_array_make(result_pool, sorted_children->nelts, - sizeof(const char *)); - for (i = 0; i < sorted_children->nelts; ++i) - APR_ARRAY_PUSH(*children, const char *) - = apr_pstrdup(result_pool, APR_ARRAY_IDX(sorted_children, i, - svn_sort__item_t).key); - - return SVN_NO_ERROR; -} - -/* Return TRUE if SRC_CHILDREN and DST_CHILDREN represent the same - children, FALSE otherwise. SRC_CHILDREN and DST_CHILDREN are - sorted arrays of basenames of type 'const char *'. */ -static svn_boolean_t -children_match(apr_array_header_t *src_children, - apr_array_header_t *dst_children) { int i; + if (children && db_kind == svn_node_dir) + { + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; - if (src_children->nelts != dst_children->nelts) - return FALSE; + *children = apr_array_make(result_pool, 16, sizeof(const char *)); + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + op_depth)); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + while (have_row) + { + const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); - for(i = 0; i < src_children->nelts; ++i) - { - const char *src_child = - APR_ARRAY_IDX(src_children, i, const char *); - const char *dst_child = - APR_ARRAY_IDX(dst_children, i, const char *); + APR_ARRAY_PUSH(*children, const char *) + = svn_relpath_basename(child_relpath, result_pool); - if (strcmp(src_child, dst_child)) - return FALSE; + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } + SVN_ERR(svn_sqlite__reset(stmt)); } + else if (children) + *children = apr_array_make(result_pool, 0, sizeof(const char *)); - return TRUE; + return SVN_NO_ERROR; } /* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties, @@ -1516,97 +1449,77 @@ props_match(svn_boolean_t *match, /* ### Drive TC_EDITOR so as to ... */ static svn_error_t * -update_moved_away_node(svn_editor_t *tc_editor, +update_moved_away_node(node_move_baton_t *nmb, + svn_wc__db_wcroot_t *wcroot, const char *src_relpath, const char *dst_relpath, - int src_op_depth, - const char *move_root_dst_relpath, - svn_revnum_t move_root_dst_revision, - svn_wc__db_t *db, - svn_wc__db_wcroot_t *wcroot, apr_pool_t *scratch_pool) { + update_move_baton_t *b = nmb->umb; svn_node_kind_t src_kind, dst_kind; const svn_checksum_t *src_checksum, *dst_checksum; apr_hash_t *src_props, *dst_props; apr_array_header_t *src_children, *dst_children; - int dst_op_depth = relpath_depth(move_root_dst_relpath); + + if (b->cancel_func) + SVN_ERR(b->cancel_func(b->cancel_baton)); SVN_ERR(get_info(&src_props, &src_checksum, &src_children, &src_kind, - src_relpath, src_op_depth, + src_relpath, b->src_op_depth, wcroot, scratch_pool, scratch_pool)); SVN_ERR(get_info(&dst_props, &dst_checksum, &dst_children, &dst_kind, - dst_relpath, dst_op_depth, + dst_relpath, b->dst_op_depth, wcroot, scratch_pool, scratch_pool)); if (src_kind == svn_node_none || (dst_kind != svn_node_none && src_kind != dst_kind)) { - SVN_ERR(svn_editor_delete(tc_editor, dst_relpath, - move_root_dst_revision)); + SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind, + scratch_pool)); } + if (nmb->skip) + return SVN_NO_ERROR; + if (src_kind != svn_node_none && src_kind != dst_kind) { if (src_kind == svn_node_file || src_kind == svn_node_symlink) { - svn_stream_t *contents; - - SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, - wcroot->abspath, src_checksum, - scratch_pool, scratch_pool)); - SVN_ERR(svn_editor_add_file(tc_editor, dst_relpath, - src_checksum, contents, src_props, - move_root_dst_revision)); + SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind, + src_checksum, src_props, scratch_pool)); } else if (src_kind == svn_node_dir) { - SVN_ERR(svn_editor_add_directory(tc_editor, dst_relpath, - src_children, src_props, - move_root_dst_revision)); + SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind, + src_props, scratch_pool)); } } else if (src_kind != svn_node_none) { - svn_boolean_t match; - apr_hash_t *props; - - SVN_ERR(props_match(&match, src_props, dst_props, scratch_pool)); - props = match ? NULL: src_props; + svn_boolean_t props_equal; + SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool)); if (src_kind == svn_node_file || src_kind == svn_node_symlink) { - svn_stream_t *contents; - - if (svn_checksum_match(src_checksum, dst_checksum)) - src_checksum = NULL; - - if (src_checksum) - SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, - wcroot->abspath, src_checksum, - scratch_pool, scratch_pool)); - else - contents = NULL; - - if (props || src_checksum) - SVN_ERR(svn_editor_alter_file(tc_editor, dst_relpath, - move_root_dst_revision, - props, src_checksum, contents)); + if (!props_equal || !svn_checksum_match(src_checksum, dst_checksum)) + SVN_ERR(tc_editor_alter_file(nmb, dst_relpath, + dst_checksum, src_checksum, + dst_props, src_props, scratch_pool)); } else if (src_kind == svn_node_dir) { - apr_array_header_t *children - = children_match(src_children, dst_children) ? NULL : src_children; - - if (props || children) - SVN_ERR(svn_editor_alter_directory(tc_editor, dst_relpath, - move_root_dst_revision, - children, props)); + if (!props_equal) + SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath, + dst_props, src_props, + scratch_pool)); } } + if (nmb->skip) + return SVN_NO_ERROR; + if (src_kind == svn_node_dir) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -1615,8 +1528,12 @@ update_moved_away_node(svn_editor_t *tc_editor, while (i < src_children->nelts || j < dst_children->nelts) { const char *child_name; - const char *src_child_relpath, *dst_child_relpath; svn_boolean_t src_only = FALSE, dst_only = FALSE; + node_move_baton_t cnmb = { 0 }; + + cnmb.pb = nmb; + cnmb.umb = nmb->umb; + cnmb.shadowed = nmb->shadowed; svn_pool_clear(iterpool); if (i >= src_children->nelts) @@ -1645,130 +1562,28 @@ update_moved_away_node(svn_editor_t *tc_editor, child_name = dst_only ? dst_name : src_name; } - src_child_relpath = svn_relpath_join(src_relpath, child_name, - iterpool); - dst_child_relpath = svn_relpath_join(dst_relpath, child_name, - iterpool); + cnmb.src_relpath = svn_relpath_join(src_relpath, child_name, + iterpool); + cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name, + iterpool); - SVN_ERR(update_moved_away_node(tc_editor, src_child_relpath, - dst_child_relpath, src_op_depth, - move_root_dst_relpath, - move_root_dst_revision, - db, wcroot, scratch_pool)); + if (!cnmb.shadowed) + SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot, + cnmb.dst_relpath, b->dst_op_depth, + iterpool)); + + SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath, + cnmb.dst_relpath, iterpool)); if (!dst_only) ++i; if (!src_only) ++j; - } - } - - return SVN_NO_ERROR; -} - -/* Update the single op-depth layer in the move destination subtree - rooted at DST_RELPATH to make it match the move source subtree - rooted at SRC_RELPATH. */ -static svn_error_t * -replace_moved_layer(const char *src_relpath, - const char *dst_relpath, - int src_op_depth, - svn_wc__db_wcroot_t *wcroot, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - int dst_op_depth = relpath_depth(dst_relpath); - /* Replace entire subtree at one op-depth. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_LOCAL_RELPATH_OP_DEPTH)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - src_relpath, src_op_depth)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - while (have_row) - { - svn_error_t *err; - svn_sqlite__stmt_t *stmt2; - const char *src_cp_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *dst_cp_relpath - = svn_relpath_join(dst_relpath, - svn_relpath_skip_ancestor(src_relpath, - src_cp_relpath), - scratch_pool); - - err = svn_sqlite__get_statement(&stmt2, wcroot->sdb, - STMT_COPY_NODE_MOVE); - if (!err) - err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id, - src_cp_relpath, src_op_depth, - dst_cp_relpath, dst_op_depth, - svn_relpath_dirname(dst_cp_relpath, - scratch_pool)); - if (!err) - err = svn_sqlite__step_done(stmt2); - if (err) - return svn_error_compose_create(err, svn_sqlite__reset(stmt)); - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); + if (nmb->skip) /* Does parent now want a skip? */ + break; + } } - SVN_ERR(svn_sqlite__reset(stmt)); - - return SVN_NO_ERROR; -} - -/* Transfer changes from the move source to the move destination. - * - * Drive the editor TC_EDITOR with the difference between DST_RELPATH - * (at its own op-depth) and SRC_RELPATH (at op-depth zero). - * - * Then update the single op-depth layer in the move destination subtree - * rooted at DST_RELPATH to make it match the move source subtree - * rooted at SRC_RELPATH. - * - * ### And the other params? - */ -static svn_error_t * -drive_tree_conflict_editor(svn_editor_t *tc_editor, - const char *src_relpath, - const char *dst_relpath, - int src_op_depth, - svn_wc_operation_t operation, - svn_wc_conflict_reason_t local_change, - svn_wc_conflict_action_t incoming_change, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, - svn_wc__db_t *db, - svn_wc__db_wcroot_t *wcroot, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - /* - * Refuse to auto-resolve unsupported tree conflicts. - */ - /* ### Only handle conflicts created by update/switch operations for now. */ - if (operation != svn_wc_operation_update && - operation != svn_wc_operation_switch) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("Cannot auto-resolve tree-conflict on '%s'"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, - src_relpath, scratch_pool), - scratch_pool)); - - /* We walk the move source (i.e. the post-update tree), comparing each node - * with the equivalent node at the move destination and applying the update - * to nodes at the move destination. */ - SVN_ERR(update_moved_away_node(tc_editor, src_relpath, dst_relpath, - src_op_depth, - dst_relpath, old_version->peg_rev, - db, wcroot, scratch_pool)); - - SVN_ERR(replace_moved_layer(src_relpath, dst_relpath, src_op_depth, - wcroot, scratch_pool)); - - SVN_ERR(svn_editor_complete(tc_editor)); return SVN_NO_ERROR; } @@ -1788,14 +1603,13 @@ suitable_for_move(svn_wc__db_wcroot_t *wcroot, STMT_SELECT_BASE_NODE)); SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - { - revision = svn_sqlite__column_revnum(stmt, 4); - repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); - } - SVN_ERR(svn_sqlite__reset(stmt)); if (!have_row) - return SVN_NO_ERROR; /* Return an error? */ + return svn_error_trace(svn_sqlite__reset(stmt)); + + revision = svn_sqlite__column_revnum(stmt, 4); + repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); + + SVN_ERR(svn_sqlite__reset(stmt)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_REPOS_PATH_REVISION)); @@ -1816,22 +1630,17 @@ suitable_for_move(svn_wc__db_wcroot_t *wcroot, svn_sqlite__reset(stmt), _("Cannot apply update because move source " "%s' is a mixed-revision working copy"), - svn_dirent_local_style(svn_dirent_join( - wcroot->abspath, - local_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, local_relpath, + scratch_pool)); if (strcmp(relpath, svn_sqlite__column_text(stmt, 1, NULL))) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, svn_sqlite__reset(stmt), _("Cannot apply update because move source " "'%s' is a switched subtree"), - svn_dirent_local_style(svn_dirent_join( - wcroot->abspath, - local_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, + local_relpath, + scratch_pool)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } @@ -1845,96 +1654,113 @@ suitable_for_move(svn_wc__db_wcroot_t *wcroot, /* The body of svn_wc__db_update_moved_away_conflict_victim(), which see. */ static svn_error_t * -update_moved_away_conflict_victim(svn_wc__db_t *db, +update_moved_away_conflict_victim(svn_revnum_t *old_rev, + svn_revnum_t *new_rev, + svn_wc__db_t *db, svn_wc__db_wcroot_t *wcroot, - const char *victim_relpath, + const char *local_relpath, + const char *delete_relpath, svn_wc_operation_t operation, - svn_wc_conflict_reason_t local_change, - svn_wc_conflict_action_t incoming_change, - const char *move_src_op_root_relpath, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { - svn_editor_t *tc_editor; - struct tc_editor_baton *tc_editor_baton; - svn_sqlite__stmt_t *stmt; - svn_boolean_t have_row; - const char *dummy1, *dummy2, *dummy3; - int src_op_depth; - const char *move_root_dst_abspath; + update_move_baton_t umb = { NULL }; + const char *src_relpath, *dst_relpath; + svn_wc_conflict_version_t old_version; + svn_wc_conflict_version_t new_version; + apr_int64_t repos_id; + node_move_baton_t nmb = { 0 }; - /* ### assumes wc write lock already held */ + SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath)); /* Construct editor baton. */ - tc_editor_baton = apr_pcalloc(scratch_pool, sizeof(*tc_editor_baton)); - SVN_ERR(svn_wc__db_op_depth_moved_to( - &dummy1, &tc_editor_baton->move_root_dst_relpath, &dummy2, &dummy3, - relpath_depth(move_src_op_root_relpath) - 1, - wcroot, victim_relpath, scratch_pool, scratch_pool)); - if (tc_editor_baton->move_root_dst_relpath == NULL) + + SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot, + local_relpath, relpath_depth(delete_relpath), + scratch_pool)); + + SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL, + wcroot, local_relpath, + umb.src_op_depth, + scratch_pool, scratch_pool)); + + if (dst_relpath == NULL) return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("The node '%s' has not been moved away"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, victim_relpath, - scratch_pool), - scratch_pool)); + path_for_error_message(wcroot, local_relpath, + scratch_pool)); - move_root_dst_abspath - = svn_dirent_join(wcroot->abspath, tc_editor_baton->move_root_dst_relpath, - scratch_pool); - SVN_ERR(svn_wc__write_check(db, move_root_dst_abspath, scratch_pool)); + umb.dst_op_depth = relpath_depth(dst_relpath); - tc_editor_baton->operation = operation; - tc_editor_baton->old_version= old_version; - tc_editor_baton->new_version= new_version; - tc_editor_baton->db = db; - tc_editor_baton->wcroot = wcroot; - tc_editor_baton->result_pool = scratch_pool; + SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool)); + SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool)); - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_HIGHEST_WORKING_NODE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - move_src_op_root_relpath, - relpath_depth(move_src_op_root_relpath))); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - if (have_row) - src_op_depth = svn_sqlite__column_int(stmt, 0); - SVN_ERR(svn_sqlite__reset(stmt)); - if (!have_row) - return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, - _("'%s' is not deleted"), - svn_dirent_local_style( - svn_dirent_join(wcroot->abspath, victim_relpath, - scratch_pool), - scratch_pool)); - if (src_op_depth == 0) - SVN_ERR(suitable_for_move(wcroot, victim_relpath, scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_version.node_kind, + &new_version.peg_rev, + &new_version.path_in_repos, &repos_id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, src_relpath, umb.src_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url, + &new_version.repos_uuid, + wcroot, repos_id, + scratch_pool)); + + SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind, + &old_version.peg_rev, + &old_version.path_in_repos, &repos_id, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + wcroot, dst_relpath, umb.dst_op_depth, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url, + &old_version.repos_uuid, + wcroot, repos_id, + scratch_pool)); + *old_rev = old_version.peg_rev; + *new_rev = new_version.peg_rev; + + umb.operation = operation; + umb.old_version= &old_version; + umb.new_version= &new_version; + umb.db = db; + umb.wcroot = wcroot; + umb.cancel_func = cancel_func; + umb.cancel_baton = cancel_baton; + + if (umb.src_op_depth == 0) + SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool)); /* Create a new, and empty, list for notification information. */ SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); - /* Create the editor... */ - SVN_ERR(svn_editor_create(&tc_editor, tc_editor_baton, - cancel_func, cancel_baton, - scratch_pool, scratch_pool)); - SVN_ERR(svn_editor_setcb_many(tc_editor, &editor_ops, scratch_pool)); - - /* ... and drive it. */ - SVN_ERR(drive_tree_conflict_editor(tc_editor, - victim_relpath, - tc_editor_baton->move_root_dst_relpath, - src_op_depth, - operation, - local_change, incoming_change, - tc_editor_baton->old_version, - tc_editor_baton->new_version, - db, wcroot, - cancel_func, cancel_baton, - scratch_pool)); + + /* Drive the editor... */ + + nmb.umb = &umb; + nmb.src_relpath = src_relpath; + nmb.dst_relpath = dst_relpath; + /* nmb.shadowed = FALSE; */ + /* nmb.edited = FALSE; */ + /* nmb.skip_children = FALSE; */ + + /* We walk the move source (i.e. the post-update tree), comparing each node + * with the equivalent node at the move destination and applying the update + * to nodes at the move destination. */ + SVN_ERR(update_moved_away_node(&nmb, wcroot, src_relpath, dst_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath, + umb.src_op_depth, + dst_relpath, NULL, NULL, + scratch_pool)); return SVN_NO_ERROR; } @@ -1942,58 +1768,43 @@ update_moved_away_conflict_victim(svn_wc__db_t *db, svn_error_t * svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, - const char *victim_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, + const char *local_abspath, + const char *delete_op_abspath, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; + svn_revnum_t old_rev, new_rev; const char *local_relpath; - svn_wc_operation_t operation; - svn_wc_conflict_reason_t local_change; - svn_wc_conflict_action_t incoming_change; - svn_wc_conflict_version_t *old_version; - svn_wc_conflict_version_t *new_version; - const char *move_src_op_root_abspath, *move_src_op_root_relpath; + const char *delete_relpath; /* ### Check for mixed-rev src or dst? */ - SVN_ERR(get_tc_info(&operation, &local_change, &incoming_change, - &move_src_op_root_abspath, - &old_version, &new_version, - db, victim_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_wc__write_check(db, move_src_op_root_abspath, scratch_pool)); - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, - db, victim_abspath, + db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - move_src_op_root_relpath - = svn_dirent_skip_ancestor(wcroot->abspath, move_src_op_root_abspath); + delete_relpath + = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath); SVN_WC__DB_WITH_TXN( update_moved_away_conflict_victim( - db, wcroot, local_relpath, - operation, local_change, incoming_change, - move_src_op_root_relpath, - old_version, new_version, + &old_rev, &new_rev, + db, wcroot, local_relpath, delete_relpath, + operation, action, reason, cancel_func, cancel_baton, scratch_pool), wcroot); /* Send all queued up notifications. */ - SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, - (old_version - ? old_version->peg_rev - : SVN_INVALID_REVNUM), - (new_version - ? new_version->peg_rev - : SVN_INVALID_REVNUM), + SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev, notify_func, notify_baton, scratch_pool)); if (notify_func) @@ -2008,7 +1819,7 @@ svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, notify->kind = svn_node_none; notify->content_state = svn_wc_notify_state_inapplicable; notify->prop_state = svn_wc_notify_state_inapplicable; - notify->revision = new_version->peg_rev; + notify->revision = new_rev; notify_func(notify_baton, notify, scratch_pool); } @@ -2017,11 +1828,12 @@ svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, } /* Set *CAN_BUMP to TRUE if DEPTH is sufficient to cover the entire - BASE tree at LOCAL_RELPATH, to FALSE otherwise. */ + tree LOCAL_RELPATH at OP_DEPTH, to FALSE otherwise. */ static svn_error_t * depth_sufficient_to_bump(svn_boolean_t *can_bump, - const char *local_relpath, svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, svn_depth_t depth, apr_pool_t *scratch_pool) { @@ -2038,21 +1850,21 @@ depth_sufficient_to_bump(svn_boolean_t *can_bump, SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_OP_DEPTH_CHILDREN)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, - local_relpath, 0)); + local_relpath, op_depth)); break; case svn_depth_files: SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_HAS_NON_FILE_CHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, - local_relpath)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, op_depth)); break; case svn_depth_immediates: SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_HAS_GRANDCHILDREN)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, - local_relpath)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, + local_relpath, op_depth)); break; default: SVN_ERR_MALFUNCTION(); @@ -2068,6 +1880,7 @@ depth_sufficient_to_bump(svn_boolean_t *can_bump, static svn_error_t * bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, const char *move_src_root_relpath, + int src_op_depth, const char *move_src_op_root_relpath, const char *move_dst_op_root_relpath, svn_wc__db_t *db, @@ -2084,18 +1897,29 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, svn_node_kind_t new_kind; svn_wc_conflict_version_t *old_version; svn_wc_conflict_version_t *new_version; + svn_skel_t *conflict; + + /* Verify precondition: We are allowed to set a tree conflict here. */ + SVN_ERR(verify_write_lock(wcroot, move_src_root_relpath, scratch_pool)); /* Read new (post-update) information from the new move source BASE node. */ - SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev, - &new_repos_relpath, &repos_id, - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - wcroot, move_src_op_root_relpath, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_kind, &new_rev, + &new_repos_relpath, &repos_id, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + wcroot, move_src_op_root_relpath, + src_op_depth, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, - wcroot->sdb, repos_id, scratch_pool)); + wcroot, repos_id, scratch_pool)); + + /* Read old (pre-update) information from the move destination node. - /* Read old (pre-update) information from the move destination node. */ + This potentially touches nodes that aren't locked by us, but that is not + a problem because we have a SQLite write lock here, and all sqlite + operations that affect move stability use a sqlite lock as well. + (And affecting the move itself requires a write lock on the node that + we do own the lock for: the move source) + */ SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_kind, &old_rev, &old_repos_relpath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -2103,6 +1927,21 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, relpath_depth(move_dst_op_root_relpath), scratch_pool, scratch_pool)); + if (strcmp(move_src_root_relpath, move_src_op_root_relpath)) + { + /* We have information for the op-root, but need it for the node that + we are putting the tree conflict on. Luckily we know that we have + a clean BASE */ + + const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath, + move_src_root_relpath); + + old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath, + scratch_pool); + new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath, + scratch_pool); + } + old_version = svn_wc_conflict_version_create2( repos_root_url, repos_uuid, old_repos_relpath, old_rev, old_kind, scratch_pool); @@ -2110,38 +1949,202 @@ bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, repos_root_url, repos_uuid, new_repos_relpath, new_rev, new_kind, scratch_pool); - SVN_ERR(mark_tree_conflict(move_src_root_relpath, - wcroot, db, old_version, new_version, - move_dst_op_root_relpath, - svn_wc_operation_update, - old_kind, new_kind, - old_repos_relpath, - svn_wc_conflict_reason_moved_away, - svn_wc_conflict_action_edit, - move_src_op_root_relpath, - scratch_pool)); + SVN_ERR(create_tree_conflict(&conflict, wcroot, move_src_root_relpath, + move_dst_op_root_relpath, + db, old_version, new_version, + svn_wc_operation_update, + old_kind, new_kind, + old_repos_relpath, + svn_wc_conflict_reason_moved_away, + svn_wc_conflict_action_edit, + move_src_op_root_relpath, + scratch_pool, scratch_pool)); + + SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db, + svn_wc_notify_tree_conflict, + new_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool)); return SVN_NO_ERROR; } -/* Bump LOCAL_RELPATH, and all the children of LOCAL_RELPATH, that are - moved-to at op-depth greater than OP_DEPTH. SRC_DONE is a hash - with keys that are 'const char *' relpaths that have already been - bumped. Any bumped paths are added to SRC_DONE. */ +/* Checks if SRC_RELPATH is within BUMP_DEPTH from BUMP_ROOT. Sets + * *SKIP to TRUE if the node should be skipped, otherwise to FALSE. + * Sets *SRC_DEPTH to the remaining depth at SRC_RELPATH. + */ +static svn_error_t * +check_bump_layer(svn_boolean_t *skip, + svn_depth_t *src_depth, + const char *bump_root, + svn_depth_t bump_depth, + const char *src_relpath, + svn_node_kind_t src_kind, + apr_pool_t *scratch_pool) +{ + const char *relpath; + + *skip = FALSE; + *src_depth = bump_depth; + + relpath = svn_relpath_skip_ancestor(bump_root, src_relpath); + + if (!relpath) + *skip = TRUE; + + if (bump_depth == svn_depth_infinity) + return SVN_NO_ERROR; + + if (relpath && *relpath == '\0') + return SVN_NO_ERROR; + + switch (bump_depth) + { + case svn_depth_empty: + *skip = TRUE; + break; + + case svn_depth_files: + if (src_kind != svn_node_file) + { + *skip = TRUE; + break; + } + /* Fallthrough */ + case svn_depth_immediates: + if (!relpath || relpath_depth(relpath) > 1) + *skip = TRUE; + + *src_depth = svn_depth_empty; + break; + default: + SVN_ERR_MALFUNCTION(); + } + + return SVN_NO_ERROR; +} + +/* The guts of bump_moved_away: Determines if a move can be bumped to match + * the move origin and if so performs this bump. + */ +static svn_error_t * +bump_moved_layer(svn_boolean_t *recurse, + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int op_depth, + const char *src_relpath, + int src_del_depth, + svn_depth_t src_depth, + const char *dst_relpath, + svn_wc__db_t *db, + apr_pool_t *scratch_pool) +{ + svn_sqlite__stmt_t *stmt; + svn_boolean_t have_row; + svn_skel_t *conflict; + svn_boolean_t can_bump; + const char *src_root_relpath; + + SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool)); + + *recurse = FALSE; + + SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, + STMT_HAS_LAYER_BETWEEN)); + + SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath, + op_depth, src_del_depth)); + + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + SVN_ERR(svn_sqlite__reset(stmt)); + + if (have_row) + return SVN_NO_ERROR; + + if (op_depth == 0) + SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath, + op_depth, src_depth, scratch_pool)); + else + /* Having chosen to bump an entire BASE tree move we + always have sufficient depth to bump subtree moves. */ + can_bump = TRUE; + + /* Are we allowed to bump */ + if (can_bump) + { + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, + dst_relpath, + FALSE, scratch_pool)); + + if (!locked) + can_bump = FALSE; + } + + src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth, + scratch_pool); + + if (!can_bump) + { + SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth, + src_root_relpath, dst_relpath, + db, scratch_pool)); + + return SVN_NO_ERROR; + } + + SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, src_root_relpath, + scratch_pool, scratch_pool)); + + /* ### TODO: check this is the right sort of tree-conflict? */ + if (!conflict) + { + /* ### TODO: verify moved_here? */ + + SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool)); + + SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, + src_relpath, op_depth, + dst_relpath, NULL, NULL, + scratch_pool)); + + *recurse = TRUE; + } + + return SVN_NO_ERROR; +} + +/* Internal storage for bump_moved_away() */ +struct bump_pair_t +{ + const char *src_relpath; + const char *dst_relpath; + int src_del_op_depth; + svn_node_kind_t src_kind; +}; + +/* Bump moves of LOCAL_RELPATH and all its descendants that were + originally below LOCAL_RELPATH at op-depth OP_DEPTH. + */ static svn_error_t * bump_moved_away(svn_wc__db_wcroot_t *wcroot, const char *local_relpath, int op_depth, - apr_hash_t *src_done, svn_depth_t depth, svn_wc__db_t *db, - apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; apr_pool_t *iterpool; + int i; + apr_array_header_t *pairs = apr_array_make(scratch_pool, 32, + sizeof(struct bump_pair_t*)); + /* Build an array, as we can't execute the same Sqlite query recursively */ iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, @@ -2151,126 +2154,48 @@ bump_moved_away(svn_wc__db_wcroot_t *wcroot, SVN_ERR(svn_sqlite__step(&have_row, stmt)); while(have_row) { - svn_sqlite__stmt_t *stmt2; - const char *src_relpath, *dst_relpath; - int src_op_depth = svn_sqlite__column_int(stmt, 2); - svn_error_t *err; - svn_skel_t *conflict; - svn_depth_t src_depth = depth; - - svn_pool_clear(iterpool); + struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp)); - src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); - dst_relpath = svn_sqlite__column_text(stmt, 1, iterpool); + bp->src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); + bp->dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); + bp->src_del_op_depth = svn_sqlite__column_int(stmt, 2); + bp->src_kind = svn_sqlite__column_token(stmt, 3, kind_map); - if (depth != svn_depth_infinity) - { - svn_boolean_t skip_this_src = FALSE; - svn_node_kind_t src_kind; + APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp; - if (strcmp(src_relpath, local_relpath)) - { - switch (depth) - { - case svn_depth_empty: - skip_this_src = TRUE; - break; - case svn_depth_files: - src_kind = svn_sqlite__column_token(stmt, 3, kind_map); - if (src_kind != svn_node_file) - { - skip_this_src = TRUE; - break; - } - /* Fallthrough */ - case svn_depth_immediates: - if (strcmp(svn_relpath_dirname(src_relpath, scratch_pool), - local_relpath)) - skip_this_src = TRUE; - src_depth = svn_depth_empty; - break; - default: - SVN_ERR_MALFUNCTION(); - } - } + SVN_ERR(svn_sqlite__step(&have_row, stmt)); + } - if (skip_this_src) - { - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - continue; - } - } + SVN_ERR(svn_sqlite__reset(stmt)); - err = svn_sqlite__get_statement(&stmt2, wcroot->sdb, - STMT_HAS_LAYER_BETWEEN); - if (!err) - err = svn_sqlite__bindf(stmt2, "isdd", wcroot->wc_id, local_relpath, - op_depth, src_op_depth); - if (!err) - err = svn_sqlite__step(&have_row, stmt2); - if (!err) - err = svn_sqlite__reset(stmt2); - if (!err && !have_row) - { - svn_boolean_t can_bump; - const char *src_root_relpath = src_relpath; + for (i = 0; i < pairs->nelts; i++) + { + struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *); + svn_boolean_t skip; + svn_depth_t src_wc_depth; - if (op_depth == 0) - err = depth_sufficient_to_bump(&can_bump, src_relpath, wcroot, - src_depth, scratch_pool); - else - /* Having chosen to bump an entire BASE tree move we - always have sufficient depth to bump subtree moves. */ - can_bump = TRUE; + svn_pool_clear(iterpool); - if (!err) - { - if (!can_bump) - { - err = bump_mark_tree_conflict(wcroot, src_relpath, - src_root_relpath, dst_relpath, - db, scratch_pool); - if (err) - return svn_error_compose_create(err, - svn_sqlite__reset(stmt)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - continue; - } - while (relpath_depth(src_root_relpath) > src_op_depth) - src_root_relpath = svn_relpath_dirname(src_root_relpath, - iterpool); + SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth, + bp->src_relpath, bp->src_kind, iterpool)); - if (!svn_hash_gets(src_done, src_relpath)) - { - svn_hash_sets(src_done, - apr_pstrdup(result_pool, src_relpath), ""); - err = svn_wc__db_read_conflict_internal(&conflict, wcroot, - src_root_relpath, - iterpool, iterpool); - /* ### TODO: check this is the right sort of tree-conflict? */ - if (!err && !conflict) - { - /* ### TODO: verify moved_here? */ - err = replace_moved_layer(src_relpath, dst_relpath, - op_depth, wcroot, iterpool); - - if (!err) - err = bump_moved_away(wcroot, dst_relpath, - relpath_depth(dst_relpath), - src_done, depth, db, - result_pool, iterpool); - } - } - } + if (!skip) + { + svn_boolean_t recurse; + + SVN_ERR(bump_moved_layer(&recurse, wcroot, + local_relpath, op_depth, + bp->src_relpath, bp->src_del_op_depth, + src_wc_depth, bp->dst_relpath, + db, iterpool)); + + if (recurse) + SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath, + relpath_depth(bp->dst_relpath), + depth, db, iterpool)); } - - if (err) - return svn_error_compose_create(err, svn_sqlite__reset(stmt)); - - SVN_ERR(svn_sqlite__step(&have_row, stmt)); } - SVN_ERR(svn_sqlite__reset(stmt)); svn_pool_destroy(iterpool); @@ -2284,84 +2209,216 @@ svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot, svn_wc__db_t *db, apr_pool_t *scratch_pool) { - apr_hash_t *src_done; - SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); if (local_relpath[0] != '\0') { - const char *dummy1, *move_dst_op_root_relpath; - const char *move_src_root_relpath, *move_src_op_root_relpath; + const char *move_dst_op_root_relpath; + const char *move_src_root_relpath, *delete_relpath; + svn_error_t *err; /* Is the root of the update moved away? (Impossible for the wcroot) */ - SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath, - &move_src_root_relpath, - &move_src_op_root_relpath, 0, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - if (move_src_root_relpath) + err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath, + &move_dst_op_root_relpath, + &delete_relpath, + wcroot, local_relpath, + 0 /* BASE */, + scratch_pool, scratch_pool); + + if (err) + { + if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) + return svn_error_trace(err); + + svn_error_clear(err); + } + else if (move_src_root_relpath) { if (strcmp(move_src_root_relpath, local_relpath)) { - SVN_ERR(bump_mark_tree_conflict(wcroot, move_src_root_relpath, - move_src_op_root_relpath, - move_dst_op_root_relpath, - db, scratch_pool)); + /* An ancestor of the path that was updated is moved away. + + If we have a lock on that ancestor, we can mark a tree + conflict on it, if we don't we ignore this case. A future + update of the ancestor will handle this. */ + svn_boolean_t locked; + + SVN_ERR(svn_wc__db_wclock_owns_lock_internal( + &locked, wcroot, + move_src_root_relpath, + FALSE, scratch_pool)); + + if (locked) + { + SVN_ERR(bump_mark_tree_conflict(wcroot, + move_src_root_relpath, 0, + delete_relpath, + move_dst_op_root_relpath, + db, scratch_pool)); + } return SVN_NO_ERROR; } } } - src_done = apr_hash_make(scratch_pool); - SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, src_done, depth, db, - scratch_pool, scratch_pool)); + SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool)); return SVN_NO_ERROR; } +/* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION + * to reflect the tree conflict on the victim SRC_ABSPATH in DB. + * + * If SRC_ABSPATH is not a tree-conflict victim, return an error. + */ static svn_error_t * -resolve_delete_raise_moved_away(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_wc__db_t *db, - svn_wc_operation_t operation, - svn_wc_conflict_action_t action, - svn_wc_conflict_version_t *old_version, - svn_wc_conflict_version_t *new_version, - apr_pool_t *scratch_pool) +fetch_conflict_details(int *src_op_depth, + svn_wc_operation_t *operation, + svn_wc_conflict_action_t *action, + svn_wc_conflict_version_t **left_version, + svn_wc_conflict_version_t **right_version, + svn_wc__db_wcroot_t *wcroot, + svn_wc__db_t *db, + const char *local_relpath, + const svn_skel_t *conflict_skel, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const apr_array_header_t *locations; + svn_boolean_t text_conflicted; + svn_boolean_t prop_conflicted; + svn_boolean_t tree_conflicted; + const char *move_src_op_root_abspath; + svn_wc_conflict_reason_t reason; + const char *local_abspath = svn_dirent_join(wcroot->abspath, local_relpath, + scratch_pool); + + if (!conflict_skel) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("'%s' is not in conflict"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_info(operation, &locations, + &text_conflicted, &prop_conflicted, + &tree_conflicted, + db, local_abspath, + conflict_skel, result_pool, + scratch_pool)); + + if (text_conflicted || prop_conflicted || !tree_conflicted) + return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, + _("'%s' is not a valid tree-conflict victim"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, + action, + &move_src_op_root_abspath, + db, local_abspath, + conflict_skel, result_pool, + scratch_pool)); + + if (reason == svn_wc_conflict_reason_moved_away) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("'%s' is already a moved away tree-conflict"), + path_for_error_message(wcroot, local_relpath, + scratch_pool)); + + if (left_version) + { + if (locations && locations->nelts > 0) + *left_version = APR_ARRAY_IDX(locations, 0, + svn_wc_conflict_version_t *); + else + *left_version = NULL; + } + + if (right_version) + { + if (locations && locations->nelts > 1) + *right_version = APR_ARRAY_IDX(locations, 1, + svn_wc_conflict_version_t *); + else + *right_version = NULL; + } + + { + int del_depth = relpath_depth(local_relpath); + + if (move_src_op_root_abspath) + del_depth = relpath_depth( + svn_dirent_skip_ancestor(wcroot->abspath, + move_src_op_root_abspath)); + + SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__db_op_raise_moved_away_internal( + svn_wc__db_wcroot_t *wcroot, + const char *local_relpath, + int src_op_depth, + svn_wc__db_t *db, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + const svn_wc_conflict_version_t *old_version, + const svn_wc_conflict_version_t *new_version, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; - int op_depth = relpath_depth(local_relpath); apr_pool_t *iterpool = svn_pool_create(scratch_pool); SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_OP_DEPTH_MOVED_PAIR)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, - op_depth)); + src_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); while(have_row) { - const char *moved_relpath = svn_sqlite__column_text(stmt, 0, NULL); - const char *move_root_dst_relpath = svn_sqlite__column_text(stmt, 1, - NULL); - const char *moved_dst_repos_relpath = svn_sqlite__column_text(stmt, 2, - NULL); + svn_error_t *err; + int delete_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL); + svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map); + const char *src_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL); + const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL); + svn_skel_t *conflict; svn_pool_clear(iterpool); - SVN_ERR(mark_tree_conflict(moved_relpath, - wcroot, db, old_version, new_version, - move_root_dst_relpath, operation, - svn_node_dir /* ### ? */, - svn_node_dir /* ### ? */, - moved_dst_repos_relpath, + SVN_ERR_ASSERT(src_repos_relpath != NULL); + + err = create_tree_conflict(&conflict, wcroot, src_relpath, dst_relpath, + db, old_version, new_version, operation, + src_kind /* ### old kind */, + src_kind /* ### new kind */, + src_repos_relpath, svn_wc_conflict_reason_moved_away, - action, local_relpath, - iterpool)); + action, + svn_relpath_prefix(src_relpath, + delete_op_depth, + iterpool), + iterpool, iterpool); + + if (!err) + err = update_move_list_add(wcroot, src_relpath, db, + svn_wc_notify_tree_conflict, + src_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + conflict, NULL, scratch_pool); + + if (err) + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); } @@ -2373,41 +2430,52 @@ resolve_delete_raise_moved_away(svn_wc__db_wcroot_t *wcroot, } svn_error_t * -svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) +svn_wc__db_op_raise_moved_away(svn_wc__db_t *db, + const char *local_abspath, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; svn_wc_operation_t operation; - svn_wc_conflict_reason_t reason; svn_wc_conflict_action_t action; - svn_wc_conflict_version_t *old_version, *new_version; + svn_wc_conflict_version_t *left_version, *right_version; + int move_src_op_depth; + svn_skel_t *conflict; SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_ERR(get_tc_info(&operation, &reason, &action, NULL, - &old_version, &new_version, - db, local_abspath, scratch_pool, scratch_pool)); - - SVN_WC__DB_WITH_TXN( - resolve_delete_raise_moved_away(wcroot, local_relpath, - db, operation, action, - old_version, new_version, - scratch_pool), + SVN_WC__DB_WITH_TXN4( + svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, + wcroot, local_relpath, + scratch_pool, scratch_pool), + fetch_conflict_details(&move_src_op_depth, + &operation, &action, + &left_version, &right_version, + wcroot, db, local_relpath, conflict, + scratch_pool, scratch_pool), + svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db, + FALSE, FALSE, TRUE, + NULL, scratch_pool), + svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath, + move_src_op_depth, + db, operation, action, + left_version, right_version, + scratch_pool), wcroot); + /* These version numbers are valid for update/switch notifications + only! */ SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, - (old_version - ? old_version->peg_rev + (left_version + ? left_version->peg_rev : SVN_INVALID_REVNUM), - (new_version - ? new_version->peg_rev + (right_version + ? right_version->peg_rev : SVN_INVALID_REVNUM), notify_func, notify_baton, scratch_pool)); @@ -2416,159 +2484,109 @@ svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db, } static svn_error_t * -break_move(svn_wc__db_wcroot_t *wcroot, - const char *src_relpath, - int src_op_depth, - const char *dst_relpath, - int dst_op_depth, - apr_pool_t *scratch_pool) -{ - svn_sqlite__stmt_t *stmt; - - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_CLEAR_MOVED_TO_RELPATH)); - SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath, - src_op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - /* This statement clears moved_here. */ - SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_UPDATE_OP_DEPTH_RECURSIVE)); - SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, - dst_relpath, dst_op_depth, dst_op_depth)); - SVN_ERR(svn_sqlite__step_done(stmt)); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_wc__db_resolve_break_moved_away_internal(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - int op_depth, - apr_pool_t *scratch_pool) -{ - const char *dummy1, *move_dst_op_root_relpath; - const char *dummy2, *move_src_op_root_relpath; - - /* We want to include the passed op-depth, but the function does a > check */ - SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath, - &dummy2, - &move_src_op_root_relpath, - op_depth - 1, - wcroot, local_relpath, - scratch_pool, scratch_pool)); - - SVN_ERR_ASSERT(move_src_op_root_relpath != NULL - && move_dst_op_root_relpath != NULL); - - SVN_ERR(break_move(wcroot, local_relpath, - relpath_depth(move_src_op_root_relpath), - move_dst_op_root_relpath, - relpath_depth(move_dst_op_root_relpath), - scratch_pool)); - - return SVN_NO_ERROR; -} - -static svn_error_t * -break_moved_away_children_internal(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *scratch_pool) +break_moved_away(svn_wc__db_wcroot_t *wcroot, + svn_wc__db_t *db, + const char *local_relpath, + int parent_src_op_depth, + apr_pool_t *scratch_pool) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; apr_pool_t *iterpool; + svn_error_t *err = NULL; SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_UPDATE_MOVE_LIST)); SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, - STMT_SELECT_MOVED_PAIR2)); - SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); + STMT_SELECT_MOVED_DESCENDANTS_SRC)); + SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, + parent_src_op_depth)); SVN_ERR(svn_sqlite__step(&have_row, stmt)); iterpool = svn_pool_create(scratch_pool); while (have_row) { - const char *src_relpath = svn_sqlite__column_text(stmt, 0, iterpool); - const char *dst_relpath = svn_sqlite__column_text(stmt, 1, iterpool); - int src_op_depth = svn_sqlite__column_int(stmt, 2); + int src_op_depth = svn_sqlite__column_int(stmt, 0); + const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL); + svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map); + const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL); svn_pool_clear(iterpool); - SVN_ERR(break_move(wcroot, src_relpath, src_op_depth, dst_relpath, - relpath_depth(dst_relpath), iterpool)); - SVN_ERR(update_move_list_add(wcroot, src_relpath, - svn_wc_notify_move_broken, - svn_node_unknown, - svn_wc_notify_state_inapplicable, - svn_wc_notify_state_inapplicable)); - SVN_ERR(svn_sqlite__step(&have_row, stmt)); - } - svn_pool_destroy(iterpool); + err = verify_write_lock(wcroot, src_relpath, iterpool); - SVN_ERR(svn_sqlite__reset(stmt)); + if (!err) + err = verify_write_lock(wcroot, dst_relpath, iterpool); - return SVN_NO_ERROR; -} + if (err) + break; -svn_error_t * -svn_wc__db_resolve_break_moved_away(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; + err = svn_error_trace( + svn_wc__db_op_break_move_internal(wcroot, + src_relpath, src_op_depth, + dst_relpath, NULL, iterpool)); - SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, - db, local_abspath, - scratch_pool, scratch_pool)); - VERIFY_USABLE_WCROOT(wcroot); + if (err) + break; - SVN_WC__DB_WITH_TXN( - svn_wc__db_resolve_break_moved_away_internal(wcroot, local_relpath, - relpath_depth(local_relpath), - scratch_pool), - wcroot); + err = svn_error_trace( + update_move_list_add(wcroot, src_relpath, db, + svn_wc_notify_move_broken, + src_kind, + svn_wc_notify_state_inapplicable, + svn_wc_notify_state_inapplicable, + NULL, NULL, scratch_pool)); - if (notify_func) - { - svn_wc_notify_t *notify; + if (err) + break; - notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath, - local_relpath, - scratch_pool), - svn_wc_notify_move_broken, - scratch_pool); - notify->kind = svn_node_unknown; - notify->content_state = svn_wc_notify_state_inapplicable; - notify->prop_state = svn_wc_notify_state_inapplicable; - notify->revision = SVN_INVALID_REVNUM; - notify_func(notify_baton, notify, scratch_pool); + SVN_ERR(svn_sqlite__step(&have_row, stmt)); } + svn_pool_destroy(iterpool); - return SVN_NO_ERROR; + return svn_error_compose_create(err, svn_sqlite__reset(stmt)); } svn_error_t * -svn_wc__db_resolve_break_moved_away_children(svn_wc__db_t *db, - const char *local_abspath, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) +svn_wc__db_op_break_moved_away(svn_wc__db_t *db, + const char *local_abspath, + const char *del_op_root_abspath, + svn_boolean_t mark_tc_resolved, + svn_wc_notify_func2_t notify_func, + void *notify_baton, + apr_pool_t *scratch_pool) { svn_wc__db_wcroot_t *wcroot; const char *local_relpath; + const char *del_relpath; + int src_op_depth; SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db, local_abspath, scratch_pool, scratch_pool)); VERIFY_USABLE_WCROOT(wcroot); - SVN_WC__DB_WITH_TXN( - break_moved_away_children_internal(wcroot, local_relpath, scratch_pool), + if (del_op_root_abspath) + del_relpath = svn_dirent_skip_ancestor(wcroot->abspath, + del_op_root_abspath); + else + del_relpath = NULL; + + + SVN_WC__DB_WITH_TXN4( + find_src_op_depth(&src_op_depth, wcroot, local_relpath, + del_relpath ? relpath_depth(del_relpath) + : relpath_depth(local_relpath), + scratch_pool), + break_moved_away(wcroot, db, local_relpath, src_op_depth, + scratch_pool), + mark_tc_resolved + ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db, + FALSE, FALSE, TRUE, + NULL, scratch_pool) + : SVN_NO_ERROR, + SVN_NO_ERROR, wcroot); SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, diff --git a/subversion/libsvn_wc/wc_db_util.c b/subversion/libsvn_wc/wc_db_util.c index a6616e4..074feff 100644 --- a/subversion/libsvn_wc/wc_db_util.c +++ b/subversion/libsvn_wc/wc_db_util.c @@ -83,7 +83,7 @@ static svn_error_t * relpath_depth_sqlite(svn_sqlite__context_t *sctx, int argc, svn_sqlite__value_t *values[], - apr_pool_t *scratch_pool) + void *baton) { const char *path = NULL; apr_int64_t depth; @@ -115,6 +115,7 @@ svn_wc__db_util_open_db(svn_sqlite__db_t **sdb, const char *sdb_fname, svn_sqlite__mode_t smode, svn_boolean_t exclusive, + apr_int32_t timeout, const char *const *my_statements, apr_pool_t *result_pool, apr_pool_t *scratch_pool) @@ -139,74 +140,15 @@ svn_wc__db_util_open_db(svn_sqlite__db_t **sdb, SVN_ERR(svn_sqlite__open(sdb, sdb_abspath, smode, my_statements ? my_statements : statements, - 0, NULL, result_pool, scratch_pool)); + 0, NULL, timeout, result_pool, scratch_pool)); if (exclusive) SVN_ERR(svn_sqlite__exec_statements(*sdb, STMT_PRAGMA_LOCKING_MODE)); SVN_ERR(svn_sqlite__create_scalar_function(*sdb, "relpath_depth", 1, + TRUE /* deterministic */, relpath_depth_sqlite, NULL)); return SVN_NO_ERROR; } - -/* Some helpful transaction helpers. - - Instead of directly using SQLite transactions, these wrappers - relieve the consumer from the need to wrap the wcroot and - local_relpath, which are almost always used within the transaction. - - This also means if we later want to implement some wc_db-specific txn - handling, we have a convenient place to do it. - */ - -/* A callback which supplies WCROOTs and LOCAL_RELPATHs. */ -typedef svn_error_t *(*db_txn_callback_t)(void *baton, - svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - apr_pool_t *scratch_pool); - -/* Baton for use with run_txn() and with_db_txn(). */ -struct txn_baton_t -{ - svn_wc__db_wcroot_t *wcroot; - const char *local_relpath; - - db_txn_callback_t cb_func; - void *cb_baton; -}; - - -/* Unwrap the sqlite transaction into a wc_db txn. - Implements svn_sqlite__transaction_callback_t. */ -static svn_error_t * -run_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool) -{ - struct txn_baton_t *tb = baton; - - return svn_error_trace( - tb->cb_func(tb->cb_baton, tb->wcroot, tb->local_relpath, scratch_pool)); -} - - -/* Run CB_FUNC in a SQLite transaction with CB_BATON, using WCROOT and - LOCAL_RELPATH. If callbacks require additional information, they may - provide it using CB_BATON. */ -svn_error_t * -svn_wc__db_with_txn(svn_wc__db_wcroot_t *wcroot, - const char *local_relpath, - svn_wc__db_txn_callback_t cb_func, - void *cb_baton, - apr_pool_t *scratch_pool) -{ - struct txn_baton_t tb; - - tb.wcroot = wcroot; - tb.local_relpath = local_relpath; - tb.cb_func = cb_func; - tb.cb_baton = cb_baton; - - return svn_error_trace( - svn_sqlite__with_lock(wcroot->sdb, run_txn, &tb, scratch_pool)); -} diff --git a/subversion/libsvn_wc/wc_db_wcroot.c b/subversion/libsvn_wc/wc_db_wcroot.c index a111073..1cfca3d 100644 --- a/subversion/libsvn_wc/wc_db_wcroot.c +++ b/subversion/libsvn_wc/wc_db_wcroot.c @@ -28,6 +28,7 @@ #include "svn_dirent_uri.h" #include "svn_hash.h" #include "svn_path.h" +#include "svn_pools.h" #include "svn_version.h" #include "wc.h" @@ -42,7 +43,7 @@ #define UNKNOWN_WC_ID ((apr_int64_t) -1) #define FORMAT_FROM_SDB (-1) - +/* #define VERIFY_ON_CLOSE */ /* Get the format version from a wc-1 directory. If it is not a working copy directory, then it sets VERSION to zero and returns no error. */ @@ -145,9 +146,8 @@ get_path_kind(svn_node_kind_t *kind, } -/* Return an error if the work queue in SDB is non-empty. */ -static svn_error_t * -verify_no_work(svn_sqlite__db_t *sdb) +svn_error_t * +svn_wc__db_verify_no_work(svn_sqlite__db_t *sdb) { svn_sqlite__stmt_t *stmt; svn_boolean_t have_row; @@ -163,6 +163,27 @@ verify_no_work(svn_sqlite__db_t *sdb) return SVN_NO_ERROR; } +#if defined(VERIFY_ON_CLOSE) && defined(SVN_DEBUG) +/* Implements svn_wc__db_verify_cb_t */ +static svn_error_t * +verify_db_cb(void *baton, + const char *wc_abspath, + const char *local_relpath, + int op_depth, + int id, + const char *msg, + apr_pool_t *scratch_pool) +{ + if (op_depth >= 0) + SVN_DBG(("DB-VRFY: %s: %s (%d): SV%04d %s", + wc_abspath, local_relpath, op_depth, id, msg)); + else + SVN_DBG(("DB-VRFY: %s: %s: SV%04d %s", + wc_abspath, local_relpath, id, msg)); + + return SVN_NO_ERROR; +} +#endif /* */ static apr_status_t @@ -173,6 +194,18 @@ close_wcroot(void *data) SVN_ERR_ASSERT_NO_RETURN(wcroot->sdb != NULL); +#if defined(VERIFY_ON_CLOSE) && defined(SVN_DEBUG) + if (getenv("SVN_CMDLINE_VERIFY_SQL_AT_CLOSE")) + { + apr_pool_t *scratch_pool = svn_pool_create(NULL); + + svn_error_clear(svn_wc__db_verify_db_full_internal( + wcroot, verify_db_cb, NULL, scratch_pool)); + + svn_pool_destroy(scratch_pool); + } +#endif + err = svn_sqlite__close(wcroot->sdb); wcroot->sdb = NULL; if (err) @@ -207,6 +240,7 @@ svn_wc__db_open(svn_wc__db_t **db, { svn_error_t *err; svn_boolean_t sqlite_exclusive = FALSE; + apr_int64_t timeout; err = svn_config_get_bool(config, &sqlite_exclusive, SVN_CONFIG_SECTION_WORKING_COPY, @@ -218,6 +252,15 @@ svn_wc__db_open(svn_wc__db_t **db, } else (*db)->exclusive = sqlite_exclusive; + + err = svn_config_get_int64(config, &timeout, + SVN_CONFIG_SECTION_WORKING_COPY, + SVN_CONFIG_OPTION_SQLITE_BUSY_TIMEOUT, + 0); + if (err || timeout < 0 || timeout > APR_INT32_MAX) + svn_error_clear(err); + else + (*db)->timeout = (apr_int32_t)timeout; } return SVN_NO_ERROR; @@ -236,8 +279,8 @@ svn_wc__db_close(svn_wc__db_t *db) hi; hi = apr_hash_next(hi)) { - svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); - const char *local_abspath = svn__apr_hash_index_key(hi); + svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); + const char *local_abspath = apr_hash_this_key(hi); if (wcroot->sdb) svn_hash_sets(roots, wcroot->abspath, wcroot); @@ -258,7 +301,6 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot, apr_int64_t wc_id, int format, svn_boolean_t verify_format, - svn_boolean_t enforce_empty_wq, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { @@ -293,11 +335,11 @@ svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot, } /* Verify that no work items exists. If they do, then our integrity is - suspect and, thus, we cannot use this database. */ - if (format >= SVN_WC__HAS_WORK_QUEUE - && (enforce_empty_wq || (format < SVN_WC__VERSION && verify_format))) + suspect and, thus, we cannot upgrade this database. */ + if (format >= SVN_WC__HAS_WORK_QUEUE && + format < SVN_WC__VERSION && verify_format) { - svn_error_t *err = verify_no_work(sdb); + svn_error_t *err = svn_wc__db_verify_no_work(sdb); if (err) { /* Special message for attempts to upgrade a 1.7-dev wc with @@ -354,7 +396,7 @@ svn_wc__db_close_many_wcroots(apr_hash_t *roots, for (hi = apr_hash_first(scratch_pool, roots); hi; hi = apr_hash_next(hi)) { - svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); + svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); apr_status_t result; result = apr_pool_cleanup_run(state_pool, wcroot, close_wcroot); @@ -594,7 +636,7 @@ svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot, as the filesystem allows. */ err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE, svn_sqlite__mode_readwrite, - db->exclusive, NULL, + db->exclusive, db->timeout, NULL, db->state_pool, scratch_pool); if (err == NULL) { @@ -711,11 +753,9 @@ try_symlink_as_dir: if (err) { if (err->apr_err == SVN_ERR_WC_CORRUPT) - return svn_error_quick_wrap( - err, apr_psprintf(scratch_pool, - _("Missing a row in WCROOT for '%s'."), - svn_dirent_local_style(original_abspath, - scratch_pool))); + return svn_error_quick_wrapf( + err, _("Missing a row in WCROOT for '%s'."), + svn_dirent_local_style(original_abspath, scratch_pool)); return svn_error_trace(err); } @@ -729,7 +769,7 @@ try_symlink_as_dir: ? symlink_wcroot_abspath : local_abspath), sdb, wc_id, format, - db->verify_format, db->enforce_empty_wq, + db->verify_format, db->state_pool, scratch_pool); if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT || err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) && @@ -804,7 +844,7 @@ try_symlink_as_dir: ? symlink_wcroot_abspath : local_abspath), NULL, UNKNOWN_WC_ID, wc_format, - db->verify_format, db->enforce_empty_wq, + db->verify_format, db->state_pool, scratch_pool)); } @@ -959,10 +999,10 @@ svn_wc__db_drop_root(svn_wc__db_t *db, hi; hi = apr_hash_next(hi)) { - svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi); + svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi); if (wcroot == root_wcroot) - svn_hash_sets(db->dir_data, svn__apr_hash_index_key(hi), NULL); + svn_hash_sets(db->dir_data, apr_hash_this_key(hi), NULL); } result = apr_pool_cleanup_run(db->state_pool, root_wcroot, close_wcroot); diff --git a/subversion/libsvn_wc/workqueue.c b/subversion/libsvn_wc/workqueue.c index b034d7d..18736cc 100644 --- a/subversion/libsvn_wc/workqueue.c +++ b/subversion/libsvn_wc/workqueue.c @@ -23,6 +23,7 @@ #include <apr_pools.h> +#include "svn_private_config.h" #include "svn_types.h" #include "svn_pools.h" #include "svn_dirent_uri.h" @@ -37,7 +38,7 @@ #include "conflicts.h" #include "translate.h" -#include "svn_private_config.h" +#include "private/svn_io_private.h" #include "private/svn_skel.h" @@ -142,8 +143,7 @@ run_base_remove(work_item_baton_t *wqb, SVN_ERR(svn_wc__db_base_remove(db, local_abspath, FALSE /* keep_as_working */, - TRUE /* queue_deletes */, - FALSE /* remove_locks */, + SVN_IS_VALID_REVNUM(not_present_rev), FALSE, not_present_rev, NULL, NULL, scratch_pool)); @@ -395,8 +395,6 @@ run_postupgrade(work_item_baton_t *wqb, const char *entries_path; const char *format_path; const char *wcroot_abspath; - const char *adm_path; - const char *temp_path; svn_error_t *err; err = svn_wc__wipe_postupgrade(wri_abspath, FALSE, @@ -410,7 +408,6 @@ run_postupgrade(work_item_baton_t *wqb, SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath, scratch_pool, scratch_pool)); - adm_path = svn_wc__adm_child(wcroot_abspath, NULL, scratch_pool); entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES, scratch_pool); format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT, @@ -421,15 +418,13 @@ run_postupgrade(work_item_baton_t *wqb, ### The order may matter for some sufficiently old clients.. but ### this code only runs during upgrade after the files had been ### removed earlier during the upgrade. */ - SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING, + SVN_ERR(svn_io_write_atomic(format_path, SVN_WC__NON_ENTRIES_STRING, sizeof(SVN_WC__NON_ENTRIES_STRING) - 1, - svn_io_file_del_none, scratch_pool)); - SVN_ERR(svn_io_file_rename(temp_path, format_path, scratch_pool)); + NULL, scratch_pool)); - SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING, + SVN_ERR(svn_io_write_atomic(entries_path, SVN_WC__NON_ENTRIES_STRING, sizeof(SVN_WC__NON_ENTRIES_STRING) - 1, - svn_io_file_del_none, scratch_pool)); - SVN_ERR(svn_io_file_rename(temp_path, entries_path, scratch_pool)); + NULL, scratch_pool)); return SVN_NO_ERROR; } @@ -474,7 +469,6 @@ run_file_install(work_item_baton_t *wqb, apr_hash_t *keywords; const char *temp_dir_abspath; svn_stream_t *dst_stream; - const char *dst_abspath; apr_int64_t val; const char *wcroot_abspath; const char *source_abspath; @@ -577,10 +571,8 @@ run_file_install(work_item_baton_t *wqb, /* Translate to a temporary file. We don't want the user seeing a partial file, nor let them muck with it while we translate. We may also need to get its TRANSLATED_SIZE before the user can monkey it. */ - SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_abspath, - temp_dir_abspath, - svn_io_file_del_none, - scratch_pool, scratch_pool)); + SVN_ERR(svn_stream__create_for_install(&dst_stream, temp_dir_abspath, + scratch_pool, scratch_pool)); /* Copy from the source to the dest, translating as we go. This will also close both streams. */ @@ -589,35 +581,11 @@ run_file_install(work_item_baton_t *wqb, scratch_pool)); /* All done. Move the file into place. */ - - { - svn_error_t *err; - - err = svn_io_file_rename(dst_abspath, local_abspath, scratch_pool); - - /* With a single db we might want to install files in a missing directory. - Simply trying this scenario on error won't do any harm and at least - one user reported this problem on IRC. */ - if (err && APR_STATUS_IS_ENOENT(err->apr_err)) - { - svn_error_t *err2; - - err2 = svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath, - scratch_pool), - scratch_pool); - - if (err2) - /* Creating directory didn't work: Return all errors */ - return svn_error_trace(svn_error_compose_create(err, err2)); - else - /* We could create a directory: retry install */ - svn_error_clear(err); - - SVN_ERR(svn_io_file_rename(dst_abspath, local_abspath, scratch_pool)); - } - else - SVN_ERR(err); - } + /* With a single db we might want to install files in a missing directory. + Simply trying this scenario on error won't do any harm and at least + one user reported this problem on IRC. */ + SVN_ERR(svn_stream__install_stream(dst_stream, local_abspath, + TRUE /* make_parents*/, scratch_pool)); /* Tweak the on-disk file according to its properties. */ #ifndef WIN32 @@ -1047,8 +1015,8 @@ svn_error_t * svn_wc__wq_build_dir_install(svn_skel_t **work_item, svn_wc__db_t *db, const char *local_abspath, - apr_pool_t *scratch_pool, - apr_pool_t *result_pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const char *local_relpath; @@ -1138,7 +1106,7 @@ run_prej_install(work_item_baton_t *wqb, SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath, local_relpath, scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath, @@ -1147,14 +1115,15 @@ run_prej_install(work_item_baton_t *wqb, scratch_pool, scratch_pool)); if (arg1->next != NULL) - prop_conflict_skel = arg1->next; + prop_conflict_skel = arg1->next; /* Before Subversion 1.9 */ else - SVN_ERR_MALFUNCTION(); /* ### wc_db can't provide it ... yet. */ + prop_conflict_skel = NULL; /* Read from DB */ /* Construct a property reject file in the temporary area. */ SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath, db, local_abspath, prop_conflict_skel, + cancel_func, cancel_baton, scratch_pool, scratch_pool)); /* ... and atomically move it into place. */ @@ -1170,21 +1139,21 @@ svn_error_t * svn_wc__wq_build_prej_install(svn_skel_t **work_item, svn_wc__db_t *db, const char *local_abspath, - svn_skel_t *conflict_skel, + /*svn_skel_t *conflict_skel,*/ apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *local_relpath; *work_item = svn_skel__make_empty_list(result_pool); - /* ### gotta have this, today */ - SVN_ERR_ASSERT(conflict_skel != NULL); - SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, local_abspath, result_pool, scratch_pool)); - if (conflict_skel != NULL) - svn_skel__prepend(conflict_skel, *work_item); + /* ### In Subversion 1.7 and 1.8 we created a legacy property conflict skel + here: + if (conflict_skel != NULL) + svn_skel__prepend(conflict_skel, *work_item); + */ svn_skel__prepend_str(local_relpath, *work_item, result_pool); svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool); @@ -1317,7 +1286,7 @@ run_set_text_conflict_markers(work_item_baton_t *wqb, /* Check if we should combine with a property conflict... */ svn_skel_t *conflicts; - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath, scratch_pool, scratch_pool)); if (! conflicts) @@ -1383,7 +1352,8 @@ run_set_property_conflict_marker(work_item_baton_t *wqb, svn_skel_t *conflicts; apr_hash_t *prop_names; - SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, + db, local_abspath, scratch_pool, scratch_pool)); if (! conflicts) @@ -1520,8 +1490,11 @@ svn_wc__wq_run(svn_wc__db_t *db, { static int count = 0; const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE"); + int count_env_val; + + SVN_ERR(svn_cstring_atoi(&count_env_val, count_env_var)); - if (count_env_var && ++count == atoi(count_env_var)) + if (count_env_var && ++count == count_env_val) return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel"); } #endif diff --git a/subversion/libsvn_wc/workqueue.h b/subversion/libsvn_wc/workqueue.h index 0617a60..630de07 100644 --- a/subversion/libsvn_wc/workqueue.h +++ b/subversion/libsvn_wc/workqueue.h @@ -73,7 +73,11 @@ extern "C" { These will be combined as appropriate, and returned in one of the above three styles. - The resulting list will be ordered: WORK_ITEM1 first, then WORK_ITEM2 */ + The resulting list will be ordered: WORK_ITEM1 first, then WORK_ITEM2. + + The result contains a shallow copy of the inputs. Allocate any + additional storage needed in RESULT_POOL. + */ svn_skel_t * svn_wc__wq_merge(svn_skel_t *work_item1, svn_skel_t *work_item2, @@ -177,22 +181,12 @@ svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item, /* Set *WORK_ITEM to a new work item that will install a property reject - file for LOCAL_ABSPATH into the working copy. The property conflicts will - be taken from CONFLICT_SKEL. - - ### Caution: Links CONFLICT_SKEL into the *WORK_ITEM, which involves - modifying *CONFLICT_SKEL. - - ### TODO: Make CONFLICT_SKEL 'const' and dup it into RESULT_POOL. - - ### TODO: If CONFLICT_SKEL is NULL, take property conflicts from wc_db - for the given DB/LOCAL_ABSPATH. + file for LOCAL_ABSPATH into the working copy. */ svn_error_t * svn_wc__wq_build_prej_install(svn_skel_t **work_item, svn_wc__db_t *db, const char *local_abspath, - svn_skel_t *conflict_skel, apr_pool_t *result_pool, apr_pool_t *scratch_pool); @@ -221,8 +215,8 @@ svn_error_t * svn_wc__wq_build_dir_install(svn_skel_t **work_item, svn_wc__db_t *db, const char *local_abspath, - apr_pool_t *scratch_pool, - apr_pool_t *result_pool); + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); svn_error_t * svn_wc__wq_build_postupgrade(svn_skel_t **work_item, |