diff options
Diffstat (limited to 'subversion/libsvn_wc/adm_ops.c')
-rw-r--r-- | subversion/libsvn_wc/adm_ops.c | 1517 |
1 files changed, 206 insertions, 1311 deletions
diff --git a/subversion/libsvn_wc/adm_ops.c b/subversion/libsvn_wc/adm_ops.c index ff72d43..a0f8061 100644 --- a/subversion/libsvn_wc/adm_ops.c +++ b/subversion/libsvn_wc/adm_ops.c @@ -32,9 +32,7 @@ #include <stdlib.h> #include <apr_pools.h> -#include <apr_tables.h> #include <apr_hash.h> -#include <apr_file_io.h> #include <apr_time.h> #include <apr_errno.h> @@ -52,14 +50,12 @@ #include "wc.h" #include "adm_files.h" -#include "props.h" -#include "translate.h" +#include "conflicts.h" #include "workqueue.h" #include "svn_private_config.h" -#include "private/svn_io_private.h" -#include "private/svn_wc_private.h" - +#include "private/svn_subr_private.h" +#include "private/svn_dep_compat.h" struct svn_wc_committed_queue_t @@ -108,11 +104,19 @@ svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue) * - 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. */ + * 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, @@ -122,30 +126,15 @@ process_committed_leaf(svn_wc__db_t *db, const svn_checksum_t *checksum, apr_pool_t *scratch_pool) { - svn_wc__db_status_t status; - svn_wc__db_kind_t kind; - const svn_checksum_t *copied_checksum; svn_revnum_t new_changed_rev = new_revnum; - svn_boolean_t have_base; - svn_boolean_t have_work; - svn_boolean_t had_props; - svn_boolean_t prop_mods; svn_skel_t *work_item = NULL; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, &copied_checksum, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, &had_props, &prop_mods, - &have_base, NULL, &have_work, - db, local_abspath, - scratch_pool, scratch_pool)); - { const char *adm_abspath; - if (kind == svn_wc__db_kind_dir) + if (kind == svn_node_dir) adm_abspath = local_abspath; else adm_abspath = svn_dirent_dirname(local_abspath, scratch_pool); @@ -155,11 +144,14 @@ process_committed_leaf(svn_wc__db_t *db, if (status == svn_wc__db_status_deleted) { return svn_error_trace( - svn_wc__db_op_remove_node( + svn_wc__db_base_remove( db, local_abspath, - (have_base && !via_recurse) + FALSE /* keep_as_working */, + FALSE /* queue_deletes */, + TRUE /* remove_locks */, + (! via_recurse) ? new_revnum : SVN_INVALID_REVNUM, - kind, + NULL, NULL, scratch_pool)); } else if (status == svn_wc__db_status_not_present) @@ -177,7 +169,7 @@ process_committed_leaf(svn_wc__db_t *db, || status == svn_wc__db_status_incomplete || status == svn_wc__db_status_added); - if (kind != svn_wc__db_kind_dir) + 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 @@ -186,9 +178,9 @@ process_committed_leaf(svn_wc__db_t *db, { /* It was copied and not modified. We must have a text base for it. And the node should have a checksum. */ - SVN_ERR_ASSERT(copied_checksum != NULL); + SVN_ERR_ASSERT(old_checksum != NULL); - checksum = copied_checksum; + checksum = old_checksum; /* Is the node completely unmodified and are we recursing? */ if (via_recurse && !prop_mods) @@ -250,23 +242,41 @@ svn_wc__process_committed_internal(svn_wc__db_t *db, const svn_wc_committed_queue_t *queue, apr_pool_t *scratch_pool) { - svn_wc__db_kind_t kind; + 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 kind after processing the node itself. The node might - have been deleted */ - SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, TRUE, 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 && kind == svn_wc__db_kind_dir) + if (recurse) { const apr_array_header_t *children; apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -281,38 +291,17 @@ svn_wc__process_committed_internal(svn_wc__db_t *db, { const char *name = APR_ARRAY_IDX(children, i, const char *); const char *this_abspath; - svn_wc__db_status_t status; + const committed_queue_item_t *cqi; svn_pool_clear(iterpool); this_abspath = svn_dirent_join(local_abspath, name, iterpool); - SVN_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, - db, this_abspath, - iterpool, iterpool)); - - /* We come to this branch since we have committed a copied tree. - svn_depth_exclude is possible in this situation. So check and - skip */ - if (status == svn_wc__db_status_excluded) - continue; - sha1_checksum = NULL; - if (kind != svn_wc__db_kind_dir && queue != NULL) - { - const committed_queue_item_t *cqi; + cqi = svn_hash_gets(queue->queue, this_abspath); - cqi = apr_hash_get(queue->queue, this_abspath, - APR_HASH_KEY_STRING); - if (cqi != NULL) - { - sha1_checksum = cqi->sha1_checksum; - } - } + 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 @@ -354,7 +343,7 @@ svn_wc__prop_array_to_hash(const apr_array_header_t *props, { const svn_prop_t *prop = APR_ARRAY_IDX(props, i, const svn_prop_t *); if (prop->value != NULL) - apr_hash_set(prophash, prop->name, APR_HASH_KEY_STRING, prop->value); + svn_hash_sets(prophash, prop->name, prop->value); } return prophash; @@ -405,7 +394,7 @@ svn_wc_queue_committed3(svn_wc_committed_queue_t *queue, cqi->sha1_checksum = sha1_checksum; cqi->new_dav_cache = svn_wc__prop_array_to_hash(wcprop_changes, queue->pool); - apr_hash_set(queue->queue, local_abspath, APR_HASH_KEY_STRING, cqi); + svn_hash_sets(queue->queue, local_abspath, cqi); return SVN_NO_ERROR; } @@ -500,16 +489,15 @@ svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue, wc_ctx->db, cqi->local_abspath, iterpool, iterpool)); - if (! apr_hash_get(run_wqs, wcroot_abspath, APR_HASH_KEY_STRING)) + if (! svn_hash_gets(run_wqs, wcroot_abspath)) { wcroot_abspath = apr_pstrdup(scratch_pool, wcroot_abspath); - apr_hash_set(run_wqs, wcroot_abspath, APR_HASH_KEY_STRING, - wcroot_abspath); + svn_hash_sets(run_wqs, wcroot_abspath, wcroot_abspath); } } /* Make sure nothing happens if this function is called again. */ - SVN_ERR(svn_hash__clear(queue->queue, iterpool)); + apr_hash_clear(queue->queue); /* Ok; everything is committed now. Now we can start calling callbacks */ @@ -534,218 +522,42 @@ svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue, return SVN_NO_ERROR; } - -/* Remove/erase PATH from the working copy. This involves deleting PATH - * from the physical filesystem. PATH is assumed to be an unversioned file - * or directory. +/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in + * its parent directory in the WC. It will have the regular properties + * provided in PROPS, or none if that is NULL. * - * If ignore_enoent is TRUE, ignore missing targets. + * If the node is a file, set its on-disk executable and read-only bits to + * match its properties and lock state, + * ### only if it has an svn:executable or svn:needs-lock property. + * ### This is to match the previous behaviour of setting its props + * afterwards by calling svn_wc_prop_set4(), but is not very clean. * - * If CANCEL_FUNC is non-null, invoke it with CANCEL_BATON at various - * points, return any error immediately. + * Sync the on-disk executable and read-only bits accordingly. */ static svn_error_t * -erase_unversioned_from_wc(const char *path, - svn_boolean_t ignore_enoent, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - svn_error_t *err; - - /* Optimize the common case: try to delete the file */ - err = svn_io_remove_file2(path, ignore_enoent, scratch_pool); - if (err) - { - /* Then maybe it was a directory? */ - svn_error_clear(err); - - err = svn_io_remove_dir2(path, ignore_enoent, cancel_func, cancel_baton, - scratch_pool); - - if (err) - { - /* We're unlikely to end up here. But we need this fallback - to make sure we report the right error *and* try the - correct deletion at least once. */ - svn_node_kind_t kind; - - svn_error_clear(err); - SVN_ERR(svn_io_check_path(path, &kind, scratch_pool)); - if (kind == svn_node_file) - SVN_ERR(svn_io_remove_file2(path, ignore_enoent, scratch_pool)); - else if (kind == svn_node_dir) - SVN_ERR(svn_io_remove_dir2(path, ignore_enoent, - cancel_func, cancel_baton, - scratch_pool)); - else if (kind == svn_node_none) - return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, - _("'%s' does not exist"), - svn_dirent_local_style(path, - scratch_pool)); - else - return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, - _("Unsupported node kind for path '%s'"), - svn_dirent_local_style(path, - scratch_pool)); - - } - } - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_wc_delete4(svn_wc_context_t *wc_ctx, - const char *local_abspath, - svn_boolean_t keep_local, - svn_boolean_t delete_unversioned_target, - svn_cancel_func_t cancel_func, - void *cancel_baton, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) -{ - apr_pool_t *pool = scratch_pool; - svn_wc__db_t *db = wc_ctx->db; - svn_error_t *err; - svn_wc__db_status_t status; - svn_wc__db_kind_t kind; - svn_boolean_t conflicted; - const apr_array_header_t *conflicts; - - 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, &conflicted, - NULL, NULL, NULL, NULL, NULL, NULL, - db, local_abspath, pool, pool); - - if (delete_unversioned_target && - err != NULL && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) - { - svn_error_clear(err); - - if (!keep_local) - SVN_ERR(erase_unversioned_from_wc(local_abspath, FALSE, - cancel_func, cancel_baton, - pool)); - return SVN_NO_ERROR; - } - else - SVN_ERR(err); - - switch (status) - { - /* svn_wc__db_status_server_excluded handled by svn_wc__db_op_delete */ - case svn_wc__db_status_excluded: - case svn_wc__db_status_not_present: - return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, - _("'%s' cannot be deleted"), - svn_dirent_local_style(local_abspath, pool)); - - /* Explicitly ignore other statii */ - default: - break; - } - - if (status == svn_wc__db_status_normal - && kind == svn_wc__db_kind_dir) - { - svn_boolean_t is_wcroot; - SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, pool)); - - if (is_wcroot) - return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, - _("'%s' is the root of a working copy and " - "cannot be deleted"), - svn_dirent_local_style(local_abspath, pool)); - } - - /* Verify if we have a write lock on the parent of this node as we might - be changing the childlist of that directory. */ - SVN_ERR(svn_wc__write_check(db, svn_dirent_dirname(local_abspath, pool), - pool)); - - /* Read conflicts, to allow deleting the markers after updating the DB */ - if (!keep_local && conflicted) - SVN_ERR(svn_wc__db_read_conflicts(&conflicts, db, local_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_wc__db_op_delete(db, local_abspath, - notify_func, notify_baton, - cancel_func, cancel_baton, - pool)); - - if (!keep_local && conflicted && conflicts != NULL) - { - int i; - - /* Do we have conflict markers that should be removed? */ - for (i = 0; i < conflicts->nelts; i++) - { - const svn_wc_conflict_description2_t *desc; - - desc = APR_ARRAY_IDX(conflicts, i, - const svn_wc_conflict_description2_t*); - - if (desc->kind == svn_wc_conflict_kind_text) - { - if (desc->base_abspath != NULL) - { - SVN_ERR(svn_io_remove_file2(desc->base_abspath, TRUE, - scratch_pool)); - } - if (desc->their_abspath != NULL) - { - SVN_ERR(svn_io_remove_file2(desc->their_abspath, TRUE, - scratch_pool)); - } - if (desc->my_abspath != NULL) - { - SVN_ERR(svn_io_remove_file2(desc->my_abspath, TRUE, - scratch_pool)); - } - } - else if (desc->kind == svn_wc_conflict_kind_property - && desc->their_abspath != NULL) - { - SVN_ERR(svn_io_remove_file2(desc->their_abspath, TRUE, - scratch_pool)); - } - } - } - - /* By the time we get here, the db knows that everything that is still at - LOCAL_ABSPATH is unversioned. */ - if (!keep_local) - { - SVN_ERR(erase_unversioned_from_wc(local_abspath, TRUE, - cancel_func, cancel_baton, - pool)); - } - - return SVN_NO_ERROR; -} - - -/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in - * its parent directory in the WC. It will have no properties. */ -static svn_error_t * add_from_disk(svn_wc__db_t *db, const char *local_abspath, svn_node_kind_t kind, - svn_wc_notify_func2_t notify_func, - void *notify_baton, + const apr_hash_t *props, apr_pool_t *scratch_pool) { if (kind == svn_node_file) { - SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, NULL, scratch_pool)); + svn_skel_t *work_item = NULL; + + if (props && (svn_prop_get_value(props, SVN_PROP_EXECUTABLE) + || svn_prop_get_value(props, SVN_PROP_NEEDS_LOCK))) + SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, props, work_item, + scratch_pool)); + if (work_item) + SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, scratch_pool)); } else { - SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, NULL, + SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, props, NULL, scratch_pool)); } @@ -768,7 +580,7 @@ check_can_add_to_parent(const char **repos_root_url, { const char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool); svn_wc__db_status_t parent_status; - svn_wc__db_kind_t parent_kind; + svn_node_kind_t parent_kind; svn_error_t *err; SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool)); @@ -801,7 +613,7 @@ check_can_add_to_parent(const char **repos_root_url, svn_dirent_local_style(local_abspath, scratch_pool)); } - else if (parent_kind != svn_wc__db_kind_dir) + else if (parent_kind != svn_node_dir) return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("Can't schedule an addition of '%s'" " below a not-directory node"), @@ -851,6 +663,7 @@ check_can_add_node(svn_node_kind_t *kind_p, const char *base_name = svn_dirent_basename(local_abspath, scratch_pool); svn_boolean_t is_wc_root; svn_node_kind_t kind; + svn_boolean_t is_special; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); SVN_ERR_ASSERT(!copyfrom_url || (svn_uri_is_canonical(copyfrom_url, @@ -867,7 +680,8 @@ check_can_add_node(svn_node_kind_t *kind_p, SVN_ERR(svn_path_check_valid(local_abspath, scratch_pool)); /* Make sure something's there; set KIND and *KIND_P. */ - SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); + SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special, + scratch_pool)); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, _("'%s' not found"), @@ -929,14 +743,20 @@ check_can_add_node(svn_node_kind_t *kind_p, SVN_ERR_ASSERT(!is_wc_root); break; case svn_wc__db_status_normal: - if (copyfrom_url) - { - SVN_ERR(svn_wc__check_wc_root(&is_wc_root, NULL, NULL, - db, local_abspath, - scratch_pool)); + SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, local_abspath, + scratch_pool)); - if (is_wc_root) - break; + if (is_wc_root && copyfrom_url) + { + /* Integrate a sub working copy in a parent working copy + (legacy behavior) */ + break; + } + else if (is_wc_root && is_special) + { + /* Adding a symlink to a working copy root. + (special_tests.py 23: externals as symlink targets) */ + break; } /* else: Fall through in default error */ @@ -1099,7 +919,7 @@ svn_wc_add4(svn_wc_context_t *wc_ctx, if (!copyfrom_url) /* Case 2a: It's a simple add */ { - SVN_ERR(add_from_disk(db, local_abspath, kind, notify_func, notify_baton, + SVN_ERR(add_from_disk(db, local_abspath, kind, NULL, scratch_pool)); if (kind == svn_node_dir && !db_row_exists) { @@ -1146,6 +966,7 @@ svn_wc_add4(svn_wc_context_t *wc_ctx, repos_root_url, repos_uuid, copyfrom_rev, NULL /* children */, depth, + FALSE /* is_move */, NULL /* conflicts */, NULL /* work items */, scratch_pool)); @@ -1172,11 +993,12 @@ svn_wc_add4(svn_wc_context_t *wc_ctx, 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, - void *notify_baton, - apr_pool_t *scratch_pool) +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_node_kind_t kind; @@ -1184,8 +1006,21 @@ svn_wc_add_from_disk(svn_wc_context_t *wc_ctx, NULL, SVN_INVALID_REVNUM, scratch_pool)); SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx->db, local_abspath, scratch_pool, scratch_pool)); - SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, - notify_func, notify_baton, + + /* Canonicalize and check the props */ + if (props) + { + apr_hash_t *new_props; + + SVN_ERR(svn_wc__canonicalize_props( + &new_props, + local_abspath, kind, props, FALSE /* skip_some_checks */, + scratch_pool, scratch_pool)); + props = new_props; + } + + /* Add to the DB and maybe update on-disk executable read-only bits */ + SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, props, scratch_pool)); /* Report the addition to the caller. */ @@ -1195,817 +1030,13 @@ svn_wc_add_from_disk(svn_wc_context_t *wc_ctx, svn_wc_notify_add, scratch_pool); notify->kind = kind; + notify->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE); (*notify_func)(notify_baton, notify, scratch_pool); } return SVN_NO_ERROR; } -/* Thoughts on Reversion. - - What does is mean to revert a given PATH in a tree? We'll - consider things by their modifications. - - Adds - - - For files, svn_wc_remove_from_revision_control(), baby. - - - Added directories may contain nothing but added children, and - reverting the addition of a directory necessarily means reverting - the addition of all the directory's children. Again, - svn_wc_remove_from_revision_control() should do the trick. - - Deletes - - - Restore properties to their unmodified state. - - - For files, restore the pristine contents, and reset the schedule - to 'normal'. - - - For directories, reset the schedule to 'normal'. All children - of a directory marked for deletion must also be marked for - deletion, but it's okay for those children to remain deleted even - if their parent directory is restored. That's what the - recursive flag is for. - - Replaces - - - Restore properties to their unmodified state. - - - For files, restore the pristine contents, and reset the schedule - to 'normal'. - - - For directories, reset the schedule to normal. A replaced - directory can have deleted children (left over from the initial - deletion), replaced children (children of the initial deletion - now re-added), and added children (new entries under the - replaced directory). Since this is technically an addition, it - necessitates recursion. - - Modifications - - - Restore properties and, for files, contents to their unmodified - state. - -*/ - - -/* Remove conflict file CONFLICT_ABSPATH, which may not exist, and set - * *NOTIFY_REQUIRED to TRUE if the file was present and removed. */ -static svn_error_t * -remove_conflict_file(svn_boolean_t *notify_required, - const char *conflict_abspath, - const char *local_abspath, - apr_pool_t *scratch_pool) -{ - if (conflict_abspath) - { - svn_error_t *err = svn_io_remove_file2(conflict_abspath, FALSE, - scratch_pool); - if (err) - svn_error_clear(err); - else - *notify_required = TRUE; - } - - return SVN_NO_ERROR; -} - - -/* Sort copied children obtained from the revert list based on - * their paths in descending order (longest paths first). */ -static int -compare_revert_list_copied_children(const void *a, const void *b) -{ - const svn_wc__db_revert_list_copied_child_info_t * const *ca = a; - const svn_wc__db_revert_list_copied_child_info_t * const *cb = b; - int i; - - i = svn_path_compare_paths(ca[0]->abspath, cb[0]->abspath); - - /* Reverse the result of svn_path_compare_paths() to achieve - * descending order. */ - return -i; -} - - -/* Remove all reverted copied children from the directory at LOCAL_ABSPATH. - * If REMOVE_SELF is TRUE, try to remove LOCAL_ABSPATH itself (REMOVE_SELF - * should be set if LOCAL_ABSPATH is itself a reverted copy). - * - * If REMOVED_SELF is not NULL, indicate in *REMOVED_SELF whether - * LOCAL_ABSPATH itself was removed. - * - * All reverted copied file children are removed from disk. Reverted copied - * directories left empty as a result are also removed from disk. - */ -static svn_error_t * -revert_restore_handle_copied_dirs(svn_boolean_t *removed_self, - svn_wc__db_t *db, - const char *local_abspath, - svn_boolean_t remove_self, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - const 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; - apr_pool_t *iterpool; - svn_error_t *err; - - if (removed_self) - *removed_self = FALSE; - - SVN_ERR(svn_wc__db_revert_list_read_copied_children(&copied_children, - db, local_abspath, - scratch_pool, - scratch_pool)); - iterpool = svn_pool_create(scratch_pool); - - /* Remove all copied file children. */ - for (i = 0; i < copied_children->nelts; i++) - { - child_info = APR_ARRAY_IDX( - copied_children, i, - svn_wc__db_revert_list_copied_child_info_t *); - - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - - if (child_info->kind != svn_wc__db_kind_file) - continue; - - svn_pool_clear(iterpool); - - /* Make sure what we delete from disk is really a file. */ - SVN_ERR(svn_io_check_path(child_info->abspath, &on_disk, iterpool)); - if (on_disk != svn_node_file) - continue; - - SVN_ERR(svn_io_remove_file2(child_info->abspath, TRUE, iterpool)); - } - - /* Delete every empty child directory. - * We cannot delete children recursively since we want to keep any files - * 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); - for (i = 0; i < copied_children->nelts; i++) - { - child_info = APR_ARRAY_IDX( - copied_children, i, - svn_wc__db_revert_list_copied_child_info_t *); - - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - - if (child_info->kind != svn_wc__db_kind_dir) - continue; - - svn_pool_clear(iterpool); - - err = svn_io_dir_remove_nonrecursive(child_info->abspath, iterpool); - if (err) - { - if (APR_STATUS_IS_ENOENT(err->apr_err) || - SVN__APR_STATUS_IS_ENOTDIR(err->apr_err) || - APR_STATUS_IS_ENOTEMPTY(err->apr_err)) - svn_error_clear(err); - else - return svn_error_trace(err); - } - } - - if (remove_self) - { - /* Delete LOCAL_ABSPATH itself if no children are left. */ - err = svn_io_dir_remove_nonrecursive(local_abspath, iterpool); - if (err) - { - if (APR_STATUS_IS_ENOTEMPTY(err->apr_err)) - svn_error_clear(err); - else - return svn_error_trace(err); - } - else if (removed_self) - *removed_self = TRUE; - } - - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - - -/* Make the working tree under LOCAL_ABSPATH to depth DEPTH match the - versioned tree. This function is called after svn_wc__db_op_revert - has done the database revert and created the revert list. Notifies - for all paths equal to or below LOCAL_ABSPATH that are reverted. */ -static svn_error_t * -revert_restore(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 *err; - svn_wc__db_status_t status; - svn_wc__db_kind_t kind; - svn_node_kind_t on_disk; - svn_boolean_t notify_required; - const char *conflict_old; - const char *conflict_new; - const char *conflict_working; - const char *prop_reject; - svn_filesize_t recorded_size; - apr_time_t recorded_mod_time; - apr_finfo_t finfo; -#ifdef HAVE_SYMLINK - svn_boolean_t special; -#endif - svn_boolean_t copied_here; - svn_wc__db_kind_t reverted_kind; - - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - - SVN_ERR(svn_wc__db_revert_list_read(¬ify_required, - &conflict_old, &conflict_new, - &conflict_working, &prop_reject, - &copied_here, &reverted_kind, - 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_mod_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) - { - svn_error_clear(err); - - if (!copied_here) - { - if (notify_func && notify_required) - notify_func(notify_baton, - svn_wc_create_notify(local_abspath, - svn_wc_notify_revert, - scratch_pool), - scratch_pool); - - if (notify_func) - SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton, - db, local_abspath, - scratch_pool)); - return SVN_NO_ERROR; - } - else - { - /* ### Initialise to values which prevent the code below from - * ### trying to restore anything to disk. - * ### 'status' should be status_unknown but that doesn't exist. */ - status = svn_wc__db_status_normal; - kind = svn_wc__db_kind_unknown; - recorded_size = SVN_INVALID_FILESIZE; - recorded_mod_time = 0; - } - } - else if (err) - return svn_error_trace(err); - - err = svn_io_stat(&finfo, local_abspath, - APR_FINFO_TYPE | APR_FINFO_LINK - | APR_FINFO_SIZE | APR_FINFO_MTIME - | SVN__APR_FINFO_EXECUTABLE - | SVN__APR_FINFO_READONLY, - scratch_pool); - - if (err && (APR_STATUS_IS_ENOENT(err->apr_err) - || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) - { - svn_error_clear(err); - on_disk = svn_node_none; -#ifdef HAVE_SYMLINK - special = FALSE; -#endif - } - else - { - if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK) - on_disk = svn_node_file; - else if (finfo.filetype == APR_DIR) - on_disk = svn_node_dir; - else - on_disk = svn_node_unknown; - -#ifdef HAVE_SYMLINK - special = (finfo.filetype == APR_LNK); -#endif - } - - if (copied_here) - { - /* The revert target itself is the op-root of a copy. */ - if (reverted_kind == svn_wc__db_kind_file && on_disk == svn_node_file) - { - SVN_ERR(svn_io_remove_file2(local_abspath, TRUE, scratch_pool)); - on_disk = svn_node_none; - } - else if (reverted_kind == svn_wc__db_kind_dir && on_disk == svn_node_dir) - { - svn_boolean_t removed; - - SVN_ERR(revert_restore_handle_copied_dirs(&removed, db, - local_abspath, TRUE, - cancel_func, cancel_baton, - scratch_pool)); - if (removed) - on_disk = svn_node_none; - } - } - - /* If we expect a versioned item to be present then check that any - item on disk matches the versioned item, if it doesn't match then - fix it or delete it. */ - if (on_disk != svn_node_none - && status != svn_wc__db_status_server_excluded - && status != svn_wc__db_status_deleted - && status != svn_wc__db_status_excluded - && status != svn_wc__db_status_not_present) - { - if (on_disk == svn_node_dir && kind != svn_wc__db_kind_dir) - { - SVN_ERR(svn_io_remove_dir2(local_abspath, FALSE, - cancel_func, cancel_baton, scratch_pool)); - on_disk = svn_node_none; - } - else if (on_disk == svn_node_file && kind != svn_wc__db_kind_file) - { -#ifdef HAVE_SYMLINK - /* Preserve symlinks pointing at directories. Changes on the - * directory node have been reverted. The symlink should remain. */ - if (!(special && kind == svn_wc__db_kind_dir)) -#endif - { - SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, scratch_pool)); - on_disk = svn_node_none; - } - } - else if (on_disk == svn_node_file) - { - svn_boolean_t modified; - apr_hash_t *props; -#ifdef HAVE_SYMLINK - svn_string_t *special_prop; -#endif - - SVN_ERR(svn_wc__db_read_pristine_props(&props, db, local_abspath, - scratch_pool, scratch_pool)); - -#ifdef HAVE_SYMLINK - special_prop = apr_hash_get(props, SVN_PROP_SPECIAL, - APR_HASH_KEY_STRING); - - if ((special_prop != NULL) != special) - { - /* File/symlink mismatch. */ - SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, scratch_pool)); - on_disk = svn_node_none; - } - else -#endif - { - /* Issue #1663 asserts that we should compare a file in its - working copy format here, but before r1101473 we would only - do that if the file was already unequal to its recorded - information. - - r1101473 removes the option of asking for a working format - compare but *also* check the recorded information first, as - that combination doesn't guarantee a stable behavior. - (See the revert_test.py: revert_reexpand_keyword) - - But to have the same issue #1663 behavior for revert as we - had in <=1.6 we only have to check the recorded information - ourselves. And we already have everything we need, because - we called stat ourselves. */ - if (recorded_size != SVN_INVALID_FILESIZE - && recorded_mod_time != 0 - && recorded_size == finfo.size - && recorded_mod_time == finfo.mtime) - { - modified = FALSE; - } - else - 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)); - on_disk = svn_node_none; - } - else - { - svn_boolean_t read_only; - svn_string_t *needs_lock_prop; - - SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo, - scratch_pool)); - - needs_lock_prop = apr_hash_get(props, SVN_PROP_NEEDS_LOCK, - APR_HASH_KEY_STRING); - if (needs_lock_prop && !read_only) - { - SVN_ERR(svn_io_set_file_read_only(local_abspath, - FALSE, scratch_pool)); - 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; - } - -#if !defined(WIN32) && !defined(__OS2__) -#ifdef HAVE_SYMLINK - if (!special) -#endif - { - svn_boolean_t executable; - svn_string_t *executable_prop; - - SVN_ERR(svn_io__is_finfo_executable(&executable, &finfo, - scratch_pool)); - executable_prop = apr_hash_get(props, SVN_PROP_EXECUTABLE, - APR_HASH_KEY_STRING); - if (executable_prop && !executable) - { - SVN_ERR(svn_io_set_file_executable(local_abspath, - TRUE, FALSE, - scratch_pool)); - notify_required = TRUE; - } - else if (!executable_prop && executable) - { - SVN_ERR(svn_io_set_file_executable(local_abspath, - FALSE, FALSE, - scratch_pool)); - notify_required = TRUE; - } - } -#endif - } - } - } - } - - /* If we expect a versioned item to be present and there is nothing - on disk then recreate it. */ - if (on_disk == svn_node_none - && status != svn_wc__db_status_server_excluded - && status != svn_wc__db_status_deleted - && status != svn_wc__db_status_excluded - && status != svn_wc__db_status_not_present) - { - if (kind == svn_wc__db_kind_dir) - SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool)); - - if (kind == svn_wc__db_kind_file) - { - 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)); - } - notify_required = TRUE; - } - - SVN_ERR(remove_conflict_file(¬ify_required, conflict_old, - local_abspath, scratch_pool)); - SVN_ERR(remove_conflict_file(¬ify_required, conflict_new, - local_abspath, scratch_pool)); - SVN_ERR(remove_conflict_file(¬ify_required, conflict_working, - local_abspath, scratch_pool)); - SVN_ERR(remove_conflict_file(¬ify_required, prop_reject, - 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_wc__db_kind_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, - 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; -} - - -/* Revert tree LOCAL_ABSPATH to depth DEPTH and notify for all - reverts. */ -static svn_error_t * -new_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 *err; - - SVN_ERR_ASSERT(depth == svn_depth_empty || depth == svn_depth_infinity); - - /* We should have a write lock on the parent of local_abspath, except - when local_abspath is the working copy root. */ - { - const char *dir_abspath; - - SVN_ERR(svn_wc__db_get_wcroot(&dir_abspath, db, local_abspath, - scratch_pool, scratch_pool)); - - if (svn_dirent_is_child(dir_abspath, local_abspath, NULL)) - dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool); - - 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); - - if (!err) - err = revert_restore(db, local_abspath, depth, - use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool); - - err = svn_error_compose_create(err, - svn_wc__db_revert_list_done(db, - local_abspath, - scratch_pool)); - - return err; -} - - -/* Revert files in LOCAL_ABSPATH to depth DEPTH that match - CHANGELIST_HASH and notify for all reverts. */ -static svn_error_t * -new_revert_changelist(svn_wc__db_t *db, - const char *local_abspath, - svn_depth_t depth, - svn_boolean_t use_commit_times, - apr_hash_t *changelist_hash, - svn_cancel_func_t cancel_func, - void *cancel_baton, - svn_wc_notify_func2_t notify_func, - void *notify_baton, - apr_pool_t *scratch_pool) -{ - apr_pool_t *iterpool; - const apr_array_header_t *children; - int i; - - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - - /* 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(new_revert_internal(db, local_abspath, - svn_depth_empty, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool)); - - if (depth == svn_depth_empty) - return SVN_NO_ERROR; - - iterpool = svn_pool_create(scratch_pool); - - /* We can handle both depth=files and depth=immediates by setting - depth=empty here. We don't need to distinguish files and - directories when making the recursive call because directories - can never match a changelist, so making the recursive call for - directories when asked for depth=files is a no-op. */ - if (depth == svn_depth_files || depth == svn_depth_immediates) - depth = svn_depth_empty; - - 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(new_revert_changelist(db, child_abspath, depth, - use_commit_times, changelist_hash, - cancel_func, cancel_baton, - notify_func, notify_baton, - iterpool)); - } - - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - - -/* Does a partially recursive revert of LOCAL_ABSPATH to depth DEPTH - (which must be either svn_depth_files or svn_depth_immediates) by - doing a non-recursive revert on each permissible path. Notifies - all reverted paths. - - ### This won't revert a copied dir with one level of children since - ### the non-recursive revert on the dir will fail. Not sure how a - ### partially recursive revert should handle actual-only nodes. */ -static svn_error_t * -new_revert_partial(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) -{ - apr_pool_t *iterpool; - const apr_array_header_t *children; - int i; - - SVN_ERR_ASSERT(depth == svn_depth_files || depth == svn_depth_immediates); - - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - - iterpool = svn_pool_create(scratch_pool); - - /* Revert the root node itself (depth=empty), then move on to the - children. */ - SVN_ERR(new_revert_internal(db, local_abspath, svn_depth_empty, - use_commit_times, cancel_func, cancel_baton, - notify_func, notify_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); - - /* For svn_depth_files: don't revert non-files. */ - if (depth == svn_depth_files) - { - svn_wc__db_kind_t kind; - - SVN_ERR(svn_wc__db_read_kind(&kind, db, child_abspath, TRUE, - iterpool)); - if (kind != svn_wc__db_kind_file) - continue; - } - - /* Revert just this node (depth=empty). */ - SVN_ERR(new_revert_internal(db, child_abspath, - svn_depth_empty, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - iterpool)); - } - - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - - -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) -{ - if (changelist_filter && changelist_filter->nelts) - { - apr_hash_t *changelist_hash; - - SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter, - scratch_pool)); - return svn_error_trace(new_revert_changelist(wc_ctx->db, local_abspath, - depth, use_commit_times, - changelist_hash, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool)); - } - - if (depth == svn_depth_empty || depth == svn_depth_infinity) - return svn_error_trace(new_revert_internal(wc_ctx->db, local_abspath, - depth, use_commit_times, - 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 - will get an error and will need to invoke an infinite revert. If - we identified those cases where svn_depth_infinity would not - revert too much we could invoke the recursive call above. */ - - if (depth == svn_depth_files || depth == svn_depth_immediates) - return svn_error_trace(new_revert_partial(wc_ctx->db, local_abspath, - depth, use_commit_times, - cancel_func, cancel_baton, - notify_func, notify_baton, - scratch_pool)); - - /* Bogus depth. Tell the caller. */ - return svn_error_create(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL, NULL); -} - - /* Return a path where nothing exists on disk, within the admin directory belonging to the WCROOT_ABSPATH directory. */ static const char * @@ -2027,7 +1058,7 @@ svn_wc_get_pristine_copy_path(const char *path, SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); - SVN_ERR(svn_wc__db_open(&db, NULL, TRUE, TRUE, pool, pool)); + SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, TRUE, pool, pool)); /* DB is now open. This is seemingly a "light" function that a caller may use repeatedly despite error return values. The rest of this function should aggressively close DB, even in the error case. */ @@ -2067,238 +1098,72 @@ svn_wc_get_pristine_contents2(svn_stream_t **contents, } -svn_error_t * -svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db, - const char *local_abspath, - svn_boolean_t destroy_wf, - svn_boolean_t instant_error, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) +typedef struct get_pristine_lazyopen_baton_t { - svn_error_t *err; - svn_boolean_t left_something = FALSE; - svn_wc__db_status_t status; - svn_wc__db_kind_t kind; - - /* ### This whole function should be rewritten to run inside a transaction, - ### to allow a stable cancel behavior. - ### - ### Subversion < 1.7 marked the directory as incomplete to allow updating - ### it from a canceled state. But this would not work because update - ### doesn't retrieve deleted items. - ### - ### WC-NG doesn't support a delete+incomplete state, but we can't build - ### transactions over multiple databases yet. */ + svn_wc_context_t *wc_ctx; + const char *wri_abspath; + const svn_checksum_t *checksum; - SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); +} get_pristine_lazyopen_baton_t; - /* Check cancellation here, so recursive calls get checked early. */ - if (cancel_func) - SVN_ERR(cancel_func(cancel_baton)); - SVN_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, - db, local_abspath, scratch_pool, scratch_pool)); - - if (kind == svn_wc__db_kind_file || kind == svn_wc__db_kind_symlink) - { - svn_boolean_t text_modified_p = FALSE; - - if (instant_error || destroy_wf) - { - svn_node_kind_t on_disk; - SVN_ERR(svn_io_check_path(local_abspath, &on_disk, scratch_pool)); - if (on_disk == svn_node_file) - { - /* Check for local mods. before removing entry */ - SVN_ERR(svn_wc__internal_file_modified_p(&text_modified_p, db, - local_abspath, FALSE, - scratch_pool)); - if (text_modified_p && instant_error) - return svn_error_createf(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL, - _("File '%s' has local modifications"), - svn_dirent_local_style(local_abspath, scratch_pool)); - } - } - - /* Remove NAME from DB */ - SVN_ERR(svn_wc__db_op_remove_node(db, local_abspath, - SVN_INVALID_REVNUM, - svn_wc__db_kind_unknown, - scratch_pool)); - - /* If we were asked to destroy the working file, do so unless - it has local mods. */ - if (destroy_wf) - { - /* Don't kill local mods. */ - if (text_modified_p) - return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL, NULL); - else /* The working file is still present; remove it. */ - SVN_ERR(svn_io_remove_file2(local_abspath, TRUE, scratch_pool)); - } - - } /* done with file case */ - else /* looking at THIS_DIR */ - { - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - const apr_array_header_t *children; - int i; - - /* ### sanity check: check 2 places for DELETED flag? */ - - /* Walk over every entry. */ - SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath, - scratch_pool, iterpool)); - - for (i = 0; i < children->nelts; i++) - { - const char *node_name = APR_ARRAY_IDX(children, i, const char*); - const char *node_abspath; - svn_wc__db_status_t node_status; - svn_wc__db_kind_t node_kind; - - svn_pool_clear(iterpool); - - node_abspath = svn_dirent_join(local_abspath, node_name, iterpool); - - SVN_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, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - db, node_abspath, - iterpool, iterpool)); - - if (node_status == svn_wc__db_status_normal - && node_kind == svn_wc__db_kind_dir) - { - svn_boolean_t is_root; - - SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL, - db, node_abspath, iterpool)); - - if (is_root) - continue; /* Just skip working copies as obstruction */ - } - - if (node_status != svn_wc__db_status_normal - && node_status != svn_wc__db_status_added - && node_status != svn_wc__db_status_incomplete) - { - /* The node is already 'deleted', so nothing to do on - versioned nodes */ - SVN_ERR(svn_wc__db_op_remove_node(db, node_abspath, - SVN_INVALID_REVNUM, - svn_wc__db_kind_unknown, - iterpool)); - - continue; - } - - err = svn_wc__internal_remove_from_revision_control( - db, node_abspath, - destroy_wf, instant_error, - cancel_func, cancel_baton, - iterpool); - - if (err && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)) - { - if (instant_error) - return svn_error_trace(err); - else - { - svn_error_clear(err); - left_something = TRUE; - } - } - else if (err) - return svn_error_trace(err); - } +/* Implements svn_stream_lazyopen_func_t */ +static svn_error_t * +get_pristine_lazyopen_func(svn_stream_t **stream, + void *baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + get_pristine_lazyopen_baton_t *b = baton; + const svn_checksum_t *sha1_checksum; - /* At this point, every directory below this one has been - removed from revision control. */ + /* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5, + we'll use it to lookup the SHA1. */ + if (b->checksum->kind == svn_checksum_sha1) + sha1_checksum = b->checksum; + else + SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, b->wc_ctx->db, + b->wri_abspath, b->checksum, + scratch_pool, scratch_pool)); - /* Remove self from parent's entries file, but only if parent is - a working copy. If it's not, that's fine, we just move on. */ - { - svn_boolean_t is_root; + SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db, + b->wri_abspath, sha1_checksum, + result_pool, scratch_pool)); + return SVN_NO_ERROR; +} - SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL, - db, local_abspath, iterpool)); +svn_error_t * +svn_wc__get_pristine_contents_by_checksum(svn_stream_t **contents, + svn_wc_context_t *wc_ctx, + const char *wri_abspath, + const svn_checksum_t *checksum, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_boolean_t present; - /* If full_path is not the top of a wc, then its parent - directory is also a working copy and has an entry for - full_path. We need to remove that entry: */ - if (! is_root) - { - SVN_ERR(svn_wc__db_op_remove_node(db, local_abspath, - SVN_INVALID_REVNUM, - svn_wc__db_kind_unknown, - iterpool)); - } - else - { - /* Remove the entire administrative .svn area, thereby removing - _this_ dir from revision control too. */ - SVN_ERR(svn_wc__adm_destroy(db, local_abspath, - cancel_func, cancel_baton, iterpool)); - } - } + *contents = NULL; - /* If caller wants us to recursively nuke everything on disk, go - ahead, provided that there are no dangling local-mod files - below */ - if (destroy_wf && (! left_something)) - { - /* If the dir is *truly* empty (i.e. has no unversioned - resources, all versioned files are gone, all .svn dirs are - gone, and contains nothing but empty dirs), then a - *non*-recursive dir_remove should work. If it doesn't, - no big deal. Just assume there are unversioned items in - there and set "left_something" */ - err = svn_io_dir_remove_nonrecursive(local_abspath, iterpool); - if (err) - { - if (!APR_STATUS_IS_ENOENT(err->apr_err)) - left_something = TRUE; - svn_error_clear(err); - } - } + SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath, + checksum, scratch_pool)); - svn_pool_destroy(iterpool); + if (present) + { + get_pristine_lazyopen_baton_t *gpl_baton; - } /* end of directory case */ + gpl_baton = apr_pcalloc(result_pool, sizeof(*gpl_baton)); + gpl_baton->wc_ctx = wc_ctx; + gpl_baton->wri_abspath = wri_abspath; + gpl_baton->checksum = checksum; - if (left_something) - return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL, NULL); + *contents = svn_stream_lazyopen_create(get_pristine_lazyopen_func, + gpl_baton, FALSE, result_pool); + } return SVN_NO_ERROR; } -svn_error_t * -svn_wc_remove_from_revision_control2(svn_wc_context_t *wc_ctx, - const char *local_abspath, - svn_boolean_t destroy_wf, - svn_boolean_t instant_error, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *scratch_pool) -{ - return svn_error_trace( - svn_wc__internal_remove_from_revision_control(wc_ctx->db, - local_abspath, - destroy_wf, - instant_error, - cancel_func, - cancel_baton, - scratch_pool)); -} - svn_error_t * svn_wc_add_lock2(svn_wc_context_t *wc_ctx, @@ -2312,6 +1177,11 @@ 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_dirent_dirname(local_abspath, scratch_pool), + scratch_pool));*/ + db_lock.token = lock->token; db_lock.owner = lock->owner; db_lock.comment = lock->comment; @@ -2331,9 +1201,19 @@ svn_wc_add_lock2(svn_wc_context_t *wc_ctx, } /* if svn:needs-lock is present, then make the file read-write. */ - SVN_ERR(svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath, - SVN_PROP_NEEDS_LOCK, scratch_pool, - scratch_pool)); + err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath, + SVN_PROP_NEEDS_LOCK, scratch_pool, + scratch_pool); + + if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS) + { + /* The node has non wc representation (e.g. deleted), so + we don't want to touch the in-wc file */ + svn_error_clear(err); + return SVN_NO_ERROR; + } + SVN_ERR(err); + if (needs_lock) SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool)); @@ -2351,6 +1231,11 @@ svn_wc_remove_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_dirent_dirname(local_abspath, scratch_pool), + scratch_pool));*/ + err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, scratch_pool); if (err) { @@ -2366,9 +1251,19 @@ svn_wc_remove_lock2(svn_wc_context_t *wc_ctx, } /* if svn:needs-lock is present, then make the file read-only. */ - SVN_ERR(svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath, - SVN_PROP_NEEDS_LOCK, scratch_pool, - scratch_pool)); + 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)); @@ -2448,6 +1343,7 @@ svn_wc_get_changelists(svn_wc_context_t *wc_ctx, apr_pool_t *scratch_pool) { struct get_cl_fn_baton gnb; + gnb.db = wc_ctx->db; gnb.clhash = NULL; gnb.callback_func = callback_func; @@ -2491,8 +1387,7 @@ svn_wc__internal_changelist_match(svn_wc__db_t *db, } return (changelist - && apr_hash_get((apr_hash_t *)clhash, changelist, - APR_HASH_KEY_STRING) != NULL); + && svn_hash_gets((apr_hash_t *)clhash, changelist) != NULL); } |