/* * node.c: routines for getting information about nodes in the working copy. * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== */ /* A note about these functions: We aren't really sure yet which bits of data libsvn_client needs about nodes. In wc-1, we just grab the entry, and then use whatever we want from it. Such a pattern is Bad. This file is intended to hold functions which retrieve specific bits of information about a node, and will hopefully give us a better idea about what data libsvn_client needs, and how to best provide that data in 1.7 final. As such, these functions should only be called from outside libsvn_wc; any internal callers are encouraged to use the appropriate information fetching function, such as svn_wc__db_read_info(). */ #include #include #include "svn_pools.h" #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_hash.h" #include "svn_types.h" #include "wc.h" #include "props.h" #include "entries.h" #include "wc_db.h" #include "svn_private_config.h" #include "private/svn_wc_private.h" /* 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(). * * 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) { apr_array_header_t *children; int i; children = apr_array_make(result_pool, rel_children->nelts, 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; } *children_abspaths = children; return SVN_NO_ERROR; } 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; SVN_ERR(svn_wc__db_read_children_of_working_node(&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__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__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)); } SVN_ERR_ASSERT(repos_root_url == NULL || *repos_root_url != NULL); SVN_ERR_ASSERT(repos_uuid == NULL || *repos_uuid != NULL); return SVN_NO_ERROR; } svn_error_t * svn_wc__node_get_repos_info(svn_revnum_t *revision, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, svn_wc_context_t *wc_ctx, const char *local_abspath, apr_pool_t *result_pool, 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)); } /* Convert DB_KIND into the appropriate NODE_KIND value. * If SHOW_HIDDEN is TRUE, report the node kind as found in the DB * even if DB_STATUS indicates that the node is hidden. * Else, return svn_node_none for such nodes. * * ### This is a bit ugly. We should consider promoting svn_kind_t * ### to the de-facto node kind type instead of converting between them * ### in non-backwards compat code. * ### See also comments at the definition of svn_kind_t. * * ### In reality, the previous comment is out of date, as there is * ### now only one enumeration for node kinds, and that is * ### svn_node_kind_t (svn_kind_t was merged with that). But it's * ### still ugly. */ static svn_error_t * convert_db_kind_to_node_kind(svn_node_kind_t *node_kind, svn_node_kind_t db_kind, svn_wc__db_status_t db_status, svn_boolean_t show_hidden) { *node_kind = db_kind; /* Make sure hidden nodes return svn_node_none. */ if (! show_hidden) switch (db_status) { case svn_wc__db_status_not_present: case svn_wc__db_status_server_excluded: case svn_wc__db_status_excluded: *node_kind = svn_node_none; default: break; } return SVN_NO_ERROR; } svn_error_t * svn_wc_read_kind2(svn_node_kind_t *kind, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t show_deleted, svn_boolean_t show_hidden, apr_pool_t *scratch_pool) { svn_node_kind_t db_kind; SVN_ERR(svn_wc__db_read_kind(&db_kind, wc_ctx->db, local_abspath, 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_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, svn_wc_context_t *wc_ctx, const char *local_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { return svn_error_trace( svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, changed_rev, changed_date, changed_author, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, wc_ctx->db, local_abspath, result_pool, scratch_pool)); } svn_error_t * svn_wc__node_get_url(const char **url, svn_wc_context_t *wc_ctx, const char *local_abspath, 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)); } /* A recursive node-walker, helper for svn_wc__internal_walk_children(). * * Call WALK_CALLBACK with WALK_BATON on all children (recursively) of * DIR_ABSPATH in DB, but not on DIR_ABSPATH itself. DIR_ABSPATH must be a * versioned directory. If SHOW_HIDDEN is true, visit hidden nodes, else * ignore them. Restrict the depth of the walk to DEPTH. * * ### Is it possible for a subdirectory to be hidden and known to be a * directory? If so, and if show_hidden is true, this will try to * recurse into it. */ static svn_error_t * walker_helper(svn_wc__db_t *db, const char *dir_abspath, svn_boolean_t show_hidden, const apr_hash_t *changelist_filter, svn_wc__node_found_func_t walk_callback, void *walk_baton, svn_depth_t depth, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { apr_hash_t *rel_children_info; apr_hash_index_t *hi; apr_pool_t *iterpool; 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); for (hi = apr_hash_first(scratch_pool, rel_children_info); hi; hi = apr_hash_next(hi)) { const char *child_name = svn__apr_hash_index_key(hi); struct svn_wc__db_walker_info_t *wi = svn__apr_hash_index_val(hi); svn_node_kind_t child_kind = wi->kind; svn_wc__db_status_t child_status = wi->status; const char *child_abspath; svn_pool_clear(iterpool); /* See if someone wants to cancel this operation. */ if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); child_abspath = svn_dirent_join(dir_abspath, child_name, iterpool); if (!show_hidden) switch (child_status) { case svn_wc__db_status_not_present: case svn_wc__db_status_server_excluded: case svn_wc__db_status_excluded: continue; default: break; } /* Return the child, if appropriate. */ if ( (child_kind == svn_node_file || depth >= svn_depth_immediates) && svn_wc__internal_changelist_match(db, child_abspath, changelist_filter, scratch_pool) ) { svn_node_kind_t kind; SVN_ERR(convert_db_kind_to_node_kind(&kind, child_kind, child_status, show_hidden)); /* ### We might want to pass child_status as well because at least * ### one callee is asking for it. * ### But is it OK to use an svn_wc__db type in this API? * ### Not yet, we need to get the node walker * ### libsvn_wc-internal first. -hkw */ SVN_ERR(walk_callback(child_abspath, kind, walk_baton, iterpool)); } /* Recurse into this directory, if appropriate. */ if (child_kind == svn_node_dir && depth >= svn_depth_immediates) { svn_depth_t depth_below_here = depth; if (depth == svn_depth_immediates) depth_below_here = svn_depth_empty; SVN_ERR(walker_helper(db, child_abspath, show_hidden, changelist_filter, walk_callback, walk_baton, depth_below_here, cancel_func, cancel_baton, iterpool)); } } svn_pool_destroy(iterpool); return SVN_NO_ERROR; } svn_error_t * svn_wc__internal_walk_children(svn_wc__db_t *db, const char *local_abspath, svn_boolean_t show_hidden, const apr_array_header_t *changelist_filter, svn_wc__node_found_func_t walk_callback, void *walk_baton, svn_depth_t walk_depth, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { svn_node_kind_t db_kind; svn_node_kind_t kind; svn_wc__db_status_t status; apr_hash_t *changelist_hash = NULL; SVN_ERR_ASSERT(walk_depth >= svn_depth_empty && walk_depth <= svn_depth_infinity); if (changelist_filter && changelist_filter->nelts) SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter, scratch_pool)); /* Check if the node exists before the first callback */ 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, 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 (db_kind == svn_node_file || status == svn_wc__db_status_not_present || status == svn_wc__db_status_excluded || status == svn_wc__db_status_server_excluded) return SVN_NO_ERROR; if (db_kind == svn_node_dir) { return svn_error_trace( walker_helper(db, local_abspath, show_hidden, changelist_hash, walk_callback, walk_baton, walk_depth, cancel_func, cancel_baton, scratch_pool)); } return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("'%s' has an unrecognized node kind"), svn_dirent_local_style(local_abspath, scratch_pool)); } 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, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t base_only, apr_pool_t *scratch_pool) { svn_wc__db_status_t status; if (base_only) { SVN_ERR(svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, wc_ctx->db, local_abspath, scratch_pool, scratch_pool)); } else { 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 (is_not_present) *is_not_present = (status == svn_wc__db_status_not_present); if (is_excluded) *is_excluded = (status == svn_wc__db_status_excluded); if (is_server_excluded) *is_server_excluded = (status == svn_wc__db_status_server_excluded); return SVN_NO_ERROR; } svn_error_t * svn_wc__node_is_added(svn_boolean_t *is_added, 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_added = (status == svn_wc__db_status_added); return SVN_NO_ERROR; } svn_error_t * svn_wc__node_has_working(svn_boolean_t *has_working, 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, has_working, wc_ctx->db, local_abspath, scratch_pool, scratch_pool)); return SVN_NO_ERROR; } svn_error_t * svn_wc__node_get_base(svn_node_kind_t *kind, svn_revnum_t *revision, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, const char **lock_token, 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) { svn_error_t *err; svn_wc__db_status_t status; svn_wc__db_lock_t *lock; svn_node_kind_t db_kind; err = svn_wc__db_base_get_info(&status, &db_kind, revision, repos_relpath, repos_root_url, repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, lock_token ? &lock : NULL, NULL, NULL, NULL, wc_ctx->db, local_abspath, result_pool, scratch_pool); 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))) { if (!ignore_enoent) { if (err) return svn_error_trace(err); else 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)); } svn_error_clear(err); if (kind) *kind = svn_node_unknown; if (revision) *revision = SVN_INVALID_REVNUM; if (repos_relpath) *repos_relpath = NULL; if (repos_root_url) *repos_root_url = NULL; if (repos_uuid) *repos_uuid = NULL; if (lock_token) *lock_token = NULL; return SVN_NO_ERROR; } if (kind) *kind = db_kind; if (lock_token) *lock_token = lock ? lock->token : NULL; SVN_ERR_ASSERT(!revision || SVN_IS_VALID_REVNUM(*revision)); SVN_ERR_ASSERT(!repos_relpath || *repos_relpath); SVN_ERR_ASSERT(!repos_root_url || *repos_root_url); SVN_ERR_ASSERT(!repos_uuid || *repos_uuid); return SVN_NO_ERROR; } svn_error_t * svn_wc__node_get_pre_ng_status_data(svn_revnum_t *revision, svn_revnum_t *changed_rev, apr_time_t *changed_date, const char **changed_author, 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; svn_boolean_t have_base, have_more_work, have_work; SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, NULL, NULL, NULL, changed_rev, changed_date, changed_author, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &have_base, &have_more_work, &have_work, wc_ctx->db, local_abspath, result_pool, scratch_pool)); if (!have_work || ((!changed_rev || SVN_IS_VALID_REVNUM(*changed_rev)) && (!revision || SVN_IS_VALID_REVNUM(*revision))) || ((status != svn_wc__db_status_added) && (status != svn_wc__db_status_deleted))) { return SVN_NO_ERROR; /* We got everything we need */ } if (have_base && !have_more_work) SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, NULL, NULL, NULL, changed_rev, changed_date, changed_author, NULL, NULL, NULL, NULL, NULL, NULL, NULL, wc_ctx->db, local_abspath, result_pool, scratch_pool)); else if (status == svn_wc__db_status_deleted) /* Check the information below a WORKING delete */ SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, changed_rev, changed_date, changed_author, NULL, NULL, NULL, NULL, NULL, wc_ctx->db, local_abspath, result_pool, scratch_pool)); return SVN_NO_ERROR; } 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) { return svn_error_trace(svn_wc__db_base_clear_dav_cache_recursive( wc_ctx->db, local_abspath, scratch_pool)); } svn_error_t * svn_wc__node_get_lock_tokens_recursive(apr_hash_t **lock_tokens, svn_wc_context_t *wc_ctx, const char *local_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { return svn_error_trace(svn_wc__db_base_get_lock_tokens_recursive( lock_tokens, wc_ctx->db, local_abspath, result_pool, scratch_pool)); } svn_error_t * svn_wc__get_excluded_subtrees(apr_hash_t **server_excluded_subtrees, svn_wc_context_t *wc_ctx, const char *local_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { return svn_error_trace( svn_wc__db_get_excluded_subtrees(server_excluded_subtrees, wc_ctx->db, local_abspath, result_pool, scratch_pool)); } svn_error_t * svn_wc__internal_get_origin(svn_boolean_t *is_copy, svn_revnum_t *revision, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, const char **copy_root_abspath, svn_wc__db_t *db, const char *local_abspath, svn_boolean_t scan_deleted, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *original_repos_relpath; const char *original_repos_root_url; const char *original_repos_uuid; svn_revnum_t original_revision; svn_wc__db_status_t status; const char *tmp_repos_relpath; 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, &original_repos_relpath, &original_repos_root_url, &original_repos_uuid, &original_revision, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_copy, db, local_abspath, result_pool, scratch_pool)); if (*repos_relpath) { return SVN_NO_ERROR; /* Returned BASE information */ } if (status == svn_wc__db_status_deleted && !scan_deleted) { if (is_copy) *is_copy = FALSE; /* Deletes are stored in working; default to FALSE */ return SVN_NO_ERROR; /* No info */ } if (original_repos_relpath) { *repos_relpath = original_repos_relpath; if (revision) *revision = original_revision; if (repos_root_url) *repos_root_url = original_repos_root_url; if (repos_uuid) *repos_uuid = original_repos_uuid; if (copy_root_abspath == NULL) return SVN_NO_ERROR; } { svn_boolean_t scan_working = FALSE; if (status == svn_wc__db_status_added) 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) { const char *op_root_abspath; SVN_ERR(svn_wc__db_scan_addition(&status, &op_root_abspath, NULL, NULL, NULL, &original_repos_relpath, repos_root_url, repos_uuid, revision, db, local_abspath, result_pool, scratch_pool)); if (status == svn_wc__db_status_added) { if (is_copy) *is_copy = FALSE; return SVN_NO_ERROR; /* Local addition */ } /* We don't know how the following error condition can be fulfilled * but we have seen that happening in the wild. Better to create * an error than a SEGFAULT. */ if (status == svn_wc__db_status_incomplete && !original_repos_relpath) return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, _("Incomplete copy information on path '%s'."), svn_dirent_local_style(local_abspath, scratch_pool)); *repos_relpath = svn_relpath_join( original_repos_relpath, svn_dirent_skip_ancestor(op_root_abspath, local_abspath), result_pool); if (copy_root_abspath) *copy_root_abspath = op_root_abspath; } else /* Deleted, excluded, not-present, server-excluded, ... */ { if (is_copy) *is_copy = FALSE; SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, repos_relpath, repos_root_url, repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, local_abspath, result_pool, scratch_pool)); } return SVN_NO_ERROR; } } svn_error_t * svn_wc__node_get_origin(svn_boolean_t *is_copy, svn_revnum_t *revision, const char **repos_relpath, const char **repos_root_url, const char **repos_uuid, const char **copy_root_abspath, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t scan_deleted, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { return svn_error_trace(svn_wc__internal_get_origin(is_copy, revision, repos_relpath, repos_root_url, repos_uuid, copy_root_abspath, wc_ctx->db, local_abspath, scan_deleted, result_pool, scratch_pool)); } svn_error_t * svn_wc__node_get_commit_status(svn_boolean_t *added, svn_boolean_t *deleted, svn_boolean_t *is_replace_root, svn_boolean_t *is_op_root, svn_revnum_t *revision, svn_revnum_t *original_revision, const char **original_repos_relpath, 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; svn_boolean_t have_base; svn_boolean_t have_more_work; svn_boolean_t op_root; /* ### All of this should be handled inside a single read transaction */ SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, original_repos_relpath, NULL, NULL, original_revision, NULL, NULL, NULL, NULL, NULL, &op_root, NULL, NULL, &have_base, &have_more_work, NULL, wc_ctx->db, local_abspath, result_pool, scratch_pool)); if (added) *added = (status == svn_wc__db_status_added); if (deleted) *deleted = (status == svn_wc__db_status_deleted); if (is_op_root) *is_op_root = op_root; if (is_replace_root) { if (status == svn_wc__db_status_added && op_root && (have_base || have_more_work)) SVN_ERR(svn_wc__db_node_check_replace(is_replace_root, NULL, NULL, wc_ctx->db, local_abspath, scratch_pool)); else *is_replace_root = FALSE; } /* Retrieve some information from BASE which is needed for replacing and/or deleting BASE nodes. */ if (have_base && !have_more_work && op_root && (revision && !SVN_IS_VALID_REVNUM(*revision))) { SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, wc_ctx->db, local_abspath, scratch_pool, scratch_pool)); } return SVN_NO_ERROR; } svn_error_t * svn_wc__node_get_md5_from_sha1(const svn_checksum_t **md5_checksum, svn_wc_context_t *wc_ctx, const char *wri_abspath, const svn_checksum_t *sha1_checksum, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { return svn_error_trace(svn_wc__db_pristine_get_md5(md5_checksum, wc_ctx->db, wri_abspath, sha1_checksum, result_pool, scratch_pool)); } svn_error_t * svn_wc__get_not_present_descendants(const apr_array_header_t **descendants, svn_wc_context_t *wc_ctx, const char *local_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { return svn_error_trace( svn_wc__db_get_not_present_descendants(descendants, wc_ctx->db, local_abspath, result_pool, scratch_pool)); } svn_error_t * svn_wc__rename_wc(svn_wc_context_t *wc_ctx, const char *from_abspath, const char *dst_abspath, apr_pool_t *scratch_pool) { const char *wcroot_abspath; SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, wc_ctx->db, from_abspath, scratch_pool, scratch_pool)); if (! strcmp(from_abspath, wcroot_abspath)) { SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, wcroot_abspath, scratch_pool)); SVN_ERR(svn_io_file_rename(from_abspath, dst_abspath, scratch_pool)); } else return svn_error_createf( SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, _("'%s' is not the root of the working copy '%s'"), svn_dirent_local_style(from_abspath, scratch_pool), svn_dirent_local_style(wcroot_abspath, scratch_pool)); return SVN_NO_ERROR; } svn_error_t * svn_wc__check_for_obstructions(svn_wc_notify_state_t *obstruction_state, svn_node_kind_t *kind, svn_boolean_t *deleted, svn_boolean_t *excluded, svn_depth_t *parent_depth, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_boolean_t no_wcroot_check, apr_pool_t *scratch_pool) { svn_wc__db_status_t status; svn_node_kind_t db_kind; svn_node_kind_t disk_kind; svn_error_t *err; *obstruction_state = svn_wc_notify_state_inapplicable; if (kind) *kind = svn_node_none; if (deleted) *deleted = FALSE; if (excluded) *excluded = FALSE; if (parent_depth) *parent_depth = svn_depth_unknown; SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool)); 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, wc_ctx->db, local_abspath, scratch_pool, scratch_pool); if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) { svn_error_clear(err); if (disk_kind != svn_node_none) { /* Nothing in the DB, but something on disk */ *obstruction_state = svn_wc_notify_state_obstructed; return SVN_NO_ERROR; } err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, parent_depth, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, wc_ctx->db, svn_dirent_dirname(local_abspath, scratch_pool), scratch_pool, scratch_pool); if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) { svn_error_clear(err); /* No versioned parent; we can't add a node here */ *obstruction_state = svn_wc_notify_state_obstructed; return SVN_NO_ERROR; } else SVN_ERR(err); if (db_kind != svn_node_dir || (status != svn_wc__db_status_normal && status != svn_wc__db_status_added)) { /* The parent doesn't allow nodes to be added below it */ *obstruction_state = svn_wc_notify_state_obstructed; } return SVN_NO_ERROR; } else SVN_ERR(err); /* Check for obstructing working copies */ if (!no_wcroot_check && db_kind == svn_node_dir && status == svn_wc__db_status_normal) { svn_boolean_t is_root; SVN_ERR(svn_wc__db_is_wcroot(&is_root, wc_ctx->db, local_abspath, scratch_pool)); if (is_root) { /* Callers should handle this as unversioned */ *obstruction_state = svn_wc_notify_state_obstructed; return SVN_NO_ERROR; } } if (kind) SVN_ERR(convert_db_kind_to_node_kind(kind, db_kind, status, FALSE)); switch (status) { case svn_wc__db_status_deleted: if (deleted) *deleted = TRUE; /* Fall through to svn_wc__db_status_not_present */ case svn_wc__db_status_not_present: if (disk_kind != svn_node_none) *obstruction_state = svn_wc_notify_state_obstructed; break; case svn_wc__db_status_excluded: case svn_wc__db_status_server_excluded: if (excluded) *excluded = TRUE; /* fall through */ case svn_wc__db_status_incomplete: *obstruction_state = svn_wc_notify_state_missing; break; case svn_wc__db_status_added: case svn_wc__db_status_normal: if (disk_kind == svn_node_none) *obstruction_state = svn_wc_notify_state_missing; else { svn_node_kind_t expected_kind; SVN_ERR(convert_db_kind_to_node_kind(&expected_kind, db_kind, status, FALSE)); if (disk_kind != expected_kind) *obstruction_state = svn_wc_notify_state_obstructed; } break; default: SVN_ERR_MALFUNCTION(); } return SVN_NO_ERROR; } svn_error_t * svn_wc__node_was_moved_away(const char **moved_to_abspath, const char **op_root_abspath, svn_wc_context_t *wc_ctx, const char *local_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_boolean_t is_deleted; 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_scan_deletion(NULL, moved_to_abspath, NULL, op_root_abspath, wc_ctx->db, local_abspath, result_pool, scratch_pool)); return SVN_NO_ERROR; } svn_error_t * svn_wc__node_was_moved_here(const char **moved_from_abspath, const char **delete_op_root_abspath, svn_wc_context_t *wc_ctx, const char *local_abspath, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_error_t *err; if (moved_from_abspath) *moved_from_abspath = NULL; if (delete_op_root_abspath) *delete_op_root_abspath = NULL; err = svn_wc__db_scan_moved(moved_from_abspath, NULL, NULL, delete_op_root_abspath, wc_ctx->db, local_abspath, result_pool, scratch_pool); if (err) { /* Return error for not added nodes */ if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS) return svn_error_trace(err); /* Path not moved here */ svn_error_clear(err); return SVN_NO_ERROR; } return SVN_NO_ERROR; }