summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/status.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/status.c')
-rw-r--r--subversion/libsvn_client/status.c767
1 files changed, 767 insertions, 0 deletions
diff --git a/subversion/libsvn_client/status.c b/subversion/libsvn_client/status.c
new file mode 100644
index 0000000000000..e581d37a41e48
--- /dev/null
+++ b/subversion/libsvn_client/status.c
@@ -0,0 +1,767 @@
+/*
+ * status.c: return the status of a working copy dirent
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+#include <apr_strings.h>
+#include <apr_pools.h>
+
+#include "svn_pools.h"
+#include "client.h"
+
+#include "svn_path.h"
+#include "svn_dirent_uri.h"
+#include "svn_delta.h"
+#include "svn_client.h"
+#include "svn_error.h"
+#include "svn_hash.h"
+
+#include "svn_private_config.h"
+#include "private/svn_wc_private.h"
+#include "private/svn_client_private.h"
+
+
+/*** Getting update information ***/
+
+/* Baton for tweak_status. It wraps a bit of extra functionality
+ around the received status func/baton, so we can remember if the
+ target was deleted in HEAD and tweak incoming status structures
+ accordingly. */
+struct status_baton
+{
+ svn_boolean_t deleted_in_repos; /* target is deleted in repos */
+ apr_hash_t *changelist_hash; /* keys are changelist names */
+ svn_client_status_func_t real_status_func; /* real status function */
+ void *real_status_baton; /* real status baton */
+ const char *anchor_abspath; /* Absolute path of anchor */
+ const char *anchor_relpath; /* Relative path of anchor */
+ svn_wc_context_t *wc_ctx; /* A working copy context. */
+};
+
+/* A status callback function which wraps the *real* status
+ function/baton. This sucker takes care of any status tweaks we
+ need to make (such as noting that the target of the status is
+ missing from HEAD in the repository).
+
+ This implements the 'svn_wc_status_func4_t' function type. */
+static svn_error_t *
+tweak_status(void *baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool)
+{
+ struct status_baton *sb = baton;
+ const char *path = local_abspath;
+ svn_client_status_t *cst;
+
+ if (sb->anchor_abspath)
+ path = svn_dirent_join(sb->anchor_relpath,
+ svn_dirent_skip_ancestor(sb->anchor_abspath, path),
+ scratch_pool);
+
+ /* If the status item has an entry, but doesn't belong to one of the
+ changelists our caller is interested in, we filter out this status
+ transmission. */
+ if (sb->changelist_hash
+ && (! status->changelist
+ || ! svn_hash_gets(sb->changelist_hash, status->changelist)))
+ {
+ return SVN_NO_ERROR;
+ }
+
+ /* If we know that the target was deleted in HEAD of the repository,
+ we need to note that fact in all the status structures that come
+ through here. */
+ if (sb->deleted_in_repos)
+ {
+ svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool);
+ new_status->repos_node_status = svn_wc_status_deleted;
+ status = new_status;
+ }
+
+ SVN_ERR(svn_client__create_status(&cst, sb->wc_ctx, local_abspath, status,
+ scratch_pool, scratch_pool));
+
+ /* Call the real status function/baton. */
+ return sb->real_status_func(sb->real_status_baton, path, cst,
+ scratch_pool);
+}
+
+/* A baton for our reporter that is used to collect locks. */
+typedef struct report_baton_t {
+ const svn_ra_reporter3_t* wrapped_reporter;
+ void *wrapped_report_baton;
+ /* The common ancestor URL of all paths included in the report. */
+ char *ancestor;
+ void *set_locks_baton;
+ svn_depth_t depth;
+ svn_client_ctx_t *ctx;
+ /* Pool to store locks in. */
+ apr_pool_t *pool;
+} report_baton_t;
+
+/* Implements svn_ra_reporter3_t->set_path. */
+static svn_error_t *
+reporter_set_path(void *report_baton, const char *path,
+ svn_revnum_t revision, svn_depth_t depth,
+ svn_boolean_t start_empty, const char *lock_token,
+ apr_pool_t *pool)
+{
+ report_baton_t *rb = report_baton;
+
+ return rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path,
+ revision, depth, start_empty,
+ lock_token, pool);
+}
+
+/* Implements svn_ra_reporter3_t->delete_path. */
+static svn_error_t *
+reporter_delete_path(void *report_baton, const char *path, apr_pool_t *pool)
+{
+ report_baton_t *rb = report_baton;
+
+ return rb->wrapped_reporter->delete_path(rb->wrapped_report_baton, path,
+ pool);
+}
+
+/* Implements svn_ra_reporter3_t->link_path. */
+static svn_error_t *
+reporter_link_path(void *report_baton, const char *path, const char *url,
+ svn_revnum_t revision, svn_depth_t depth,
+ svn_boolean_t start_empty,
+ const char *lock_token, apr_pool_t *pool)
+{
+ report_baton_t *rb = report_baton;
+
+ if (!svn_uri__is_ancestor(rb->ancestor, url))
+ {
+ const char *ancestor;
+
+ ancestor = svn_uri_get_longest_ancestor(url, rb->ancestor, pool);
+
+ /* If we got a shorter ancestor, truncate our current ancestor.
+ Note that svn_uri_get_longest_ancestor will allocate its return
+ value even if it identical to one of its arguments. */
+
+ rb->ancestor[strlen(ancestor)] = '\0';
+ rb->depth = svn_depth_infinity;
+ }
+
+ return rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url,
+ revision, depth, start_empty,
+ lock_token, pool);
+}
+
+/* Implements svn_ra_reporter3_t->finish_report. */
+static svn_error_t *
+reporter_finish_report(void *report_baton, apr_pool_t *pool)
+{
+ report_baton_t *rb = report_baton;
+ svn_ra_session_t *ras;
+ apr_hash_t *locks;
+ const char *repos_root;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ svn_error_t *err = SVN_NO_ERROR;
+
+ /* Open an RA session to our common ancestor and grab the locks under it.
+ */
+ SVN_ERR(svn_client_open_ra_session2(&ras, rb->ancestor, NULL,
+ rb->ctx, subpool, subpool));
+
+ /* The locks need to live throughout the edit. Note that if the
+ server doesn't support lock discovery, we'll just not do locky
+ stuff. */
+ err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool);
+ if (err && ((err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
+ || (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)))
+ {
+ svn_error_clear(err);
+ err = SVN_NO_ERROR;
+ locks = apr_hash_make(rb->pool);
+ }
+ SVN_ERR(err);
+
+ SVN_ERR(svn_ra_get_repos_root2(ras, &repos_root, rb->pool));
+
+ /* Close the RA session. */
+ svn_pool_destroy(subpool);
+
+ SVN_ERR(svn_wc_status_set_repos_locks(rb->set_locks_baton, locks,
+ repos_root, rb->pool));
+
+ return rb->wrapped_reporter->finish_report(rb->wrapped_report_baton, pool);
+}
+
+/* Implements svn_ra_reporter3_t->abort_report. */
+static svn_error_t *
+reporter_abort_report(void *report_baton, apr_pool_t *pool)
+{
+ report_baton_t *rb = report_baton;
+
+ return rb->wrapped_reporter->abort_report(rb->wrapped_report_baton, pool);
+}
+
+/* A reporter that keeps track of the common URL ancestor of all paths in
+ the WC and fetches repository locks for all paths under this ancestor. */
+static svn_ra_reporter3_t lock_fetch_reporter = {
+ reporter_set_path,
+ reporter_delete_path,
+ reporter_link_path,
+ reporter_finish_report,
+ reporter_abort_report
+};
+
+/* Perform status operations on each external in EXTERNAL_MAP, a const char *
+ local_abspath of all externals mapping to the const char* defining_abspath.
+ All other options are the same as those passed to svn_client_status().
+
+ If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide
+ properly formatted relative paths */
+static svn_error_t *
+do_external_status(svn_client_ctx_t *ctx,
+ apr_hash_t *external_map,
+ svn_depth_t depth,
+ svn_boolean_t get_all,
+ svn_boolean_t update,
+ svn_boolean_t no_ignore,
+ const char *anchor_abspath,
+ const char *anchor_relpath,
+ svn_client_status_func_t status_func,
+ void *status_baton,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ /* Loop over the hash of new values (we don't care about the old
+ ones). This is a mapping of versioned directories to property
+ values. */
+ for (hi = apr_hash_first(scratch_pool, external_map);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ svn_node_kind_t external_kind;
+ const char *local_abspath = svn__apr_hash_index_key(hi);
+ const char *defining_abspath = svn__apr_hash_index_val(hi);
+ svn_node_kind_t kind;
+ svn_opt_revision_t opt_rev;
+ const char *status_path;
+
+ svn_pool_clear(iterpool);
+
+ /* Obtain information on the expected external. */
+ SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
+ &opt_rev.value.number,
+ ctx->wc_ctx, defining_abspath,
+ local_abspath, FALSE,
+ iterpool, iterpool));
+
+ if (external_kind != svn_node_dir)
+ continue;
+
+ SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool));
+ if (kind != svn_node_dir)
+ continue;
+
+ if (SVN_IS_VALID_REVNUM(opt_rev.value.number))
+ opt_rev.kind = svn_opt_revision_number;
+ else
+ opt_rev.kind = svn_opt_revision_unspecified;
+
+ /* Tell the client we're starting an external status set. */
+ if (ctx->notify_func2)
+ ctx->notify_func2(
+ ctx->notify_baton2,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_status_external,
+ iterpool), iterpool);
+
+ status_path = local_abspath;
+ if (anchor_abspath)
+ {
+ status_path = svn_dirent_join(anchor_relpath,
+ svn_dirent_skip_ancestor(anchor_abspath,
+ status_path),
+ iterpool);
+ }
+
+ /* And then do the status. */
+ SVN_ERR(svn_client_status5(NULL, ctx, status_path, &opt_rev, depth,
+ get_all, update, no_ignore, FALSE, FALSE,
+ NULL, status_func, status_baton,
+ iterpool));
+ }
+
+ /* Destroy SUBPOOL and (implicitly) ITERPOOL. */
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/*** Public Interface. ***/
+
+
+svn_error_t *
+svn_client_status5(svn_revnum_t *result_rev,
+ svn_client_ctx_t *ctx,
+ const char *path,
+ const svn_opt_revision_t *revision,
+ svn_depth_t depth,
+ svn_boolean_t get_all,
+ svn_boolean_t update,
+ svn_boolean_t no_ignore,
+ svn_boolean_t ignore_externals,
+ svn_boolean_t depth_as_sticky,
+ const apr_array_header_t *changelists,
+ svn_client_status_func_t status_func,
+ void *status_baton,
+ apr_pool_t *pool) /* ### aka scratch_pool */
+{
+ struct status_baton sb;
+ const char *dir, *dir_abspath;
+ const char *target_abspath;
+ const char *target_basename;
+ apr_array_header_t *ignores;
+ svn_error_t *err;
+ apr_hash_t *changelist_hash = NULL;
+
+ if (svn_path_is_url(path))
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not a local path"), path);
+
+ if (changelists && changelists->nelts)
+ SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));
+
+ if (result_rev)
+ *result_rev = SVN_INVALID_REVNUM;
+
+ sb.real_status_func = status_func;
+ sb.real_status_baton = status_baton;
+ sb.deleted_in_repos = FALSE;
+ sb.changelist_hash = changelist_hash;
+ sb.wc_ctx = ctx->wc_ctx;
+
+ SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool));
+
+ if (update)
+ {
+ /* The status editor only works on directories, so get the ancestor
+ if necessary */
+
+ svn_node_kind_t kind;
+
+ SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath,
+ TRUE, FALSE, pool));
+
+ /* Dir must be a working copy directory or the status editor fails */
+ if (kind == svn_node_dir)
+ {
+ dir_abspath = target_abspath;
+ target_basename = "";
+ dir = path;
+ }
+ else
+ {
+ dir_abspath = svn_dirent_dirname(target_abspath, pool);
+ target_basename = svn_dirent_basename(target_abspath, NULL);
+ dir = svn_dirent_dirname(path, pool);
+
+ if (kind == svn_node_file)
+ {
+ if (depth == svn_depth_empty)
+ depth = svn_depth_files;
+ }
+ else
+ {
+ err = svn_wc_read_kind2(&kind, ctx->wc_ctx, dir_abspath,
+ FALSE, FALSE, pool);
+
+ svn_error_clear(err);
+
+ if (err || kind != svn_node_dir)
+ {
+ return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+ _("'%s' is not a working copy"),
+ svn_dirent_local_style(path, pool));
+ }
+ }
+ }
+ }
+ else
+ {
+ dir = path;
+ dir_abspath = target_abspath;
+ }
+
+ if (svn_dirent_is_absolute(dir))
+ {
+ sb.anchor_abspath = NULL;
+ sb.anchor_relpath = NULL;
+ }
+ else
+ {
+ sb.anchor_abspath = dir_abspath;
+ sb.anchor_relpath = dir;
+ }
+
+ /* Get the status edit, and use our wrapping status function/baton
+ as the callback pair. */
+ SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));
+
+ /* If we want to know about out-of-dateness, we crawl the working copy and
+ let the RA layer drive the editor for real. Otherwise, we just close the
+ edit. :-) */
+ if (update)
+ {
+ svn_ra_session_t *ra_session;
+ const char *URL;
+ svn_node_kind_t kind;
+ svn_boolean_t server_supports_depth;
+ const svn_delta_editor_t *editor;
+ void *edit_baton, *set_locks_baton;
+ svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
+
+ /* Get full URL from the ANCHOR. */
+ SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx,
+ pool, pool));
+
+ if (!URL)
+ return svn_error_createf
+ (SVN_ERR_ENTRY_MISSING_URL, NULL,
+ _("Entry '%s' has no URL"),
+ svn_dirent_local_style(dir, pool));
+
+ /* Open a repository session to the URL. */
+ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL,
+ dir_abspath, NULL,
+ FALSE, TRUE,
+ ctx, pool, pool));
+
+ SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
+ SVN_RA_CAPABILITY_DEPTH, pool));
+
+ SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton,
+ &edit_revision, ctx->wc_ctx,
+ dir_abspath, target_basename,
+ depth, get_all,
+ no_ignore, depth_as_sticky,
+ server_supports_depth,
+ ignores, tweak_status, &sb,
+ ctx->cancel_func, ctx->cancel_baton,
+ pool, pool));
+
+
+ /* Verify that URL exists in HEAD. If it doesn't, this can save
+ us a whole lot of hassle; if it does, the cost of this
+ request should be minimal compared to the size of getting
+ back the average amount of "out-of-date" information. */
+ SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM,
+ &kind, pool));
+ if (kind == svn_node_none)
+ {
+ svn_boolean_t added;
+
+ /* Our status target does not exist in HEAD. If we've got
+ it locally added, that's okay. But if it was previously
+ versioned, then it must have since been deleted from the
+ repository. (Note that "locally replaced" doesn't count
+ as "added" in this case.) */
+ SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx,
+ dir_abspath, pool));
+ if (! added)
+ sb.deleted_in_repos = TRUE;
+
+ /* And now close the edit. */
+ SVN_ERR(editor->close_edit(edit_baton, pool));
+ }
+ else
+ {
+ svn_revnum_t revnum;
+ report_baton_t rb;
+ svn_depth_t status_depth;
+
+ if (revision->kind == svn_opt_revision_head)
+ {
+ /* Cause the revision number to be omitted from the request,
+ which implies HEAD. */
+ revnum = SVN_INVALID_REVNUM;
+ }
+ else
+ {
+ /* Get a revision number for our status operation. */
+ SVN_ERR(svn_client__get_revision_number(&revnum, NULL,
+ ctx->wc_ctx,
+ target_abspath,
+ ra_session, revision,
+ pool));
+ }
+
+ if (depth_as_sticky || !server_supports_depth)
+ status_depth = depth;
+ else
+ status_depth = svn_depth_unknown; /* Use depth from WC */
+
+ /* Do the deed. Let the RA layer drive the status editor. */
+ SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
+ &rb.wrapped_report_baton,
+ target_basename, revnum, status_depth,
+ editor, edit_baton, pool));
+
+ /* Init the report baton. */
+ rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */
+ rb.set_locks_baton = set_locks_baton;
+ rb.ctx = ctx;
+ rb.pool = pool;
+
+ if (depth == svn_depth_unknown)
+ rb.depth = svn_depth_infinity;
+ else
+ rb.depth = depth;
+
+ /* Drive the reporter structure, describing the revisions
+ within PATH. When we call reporter->finish_report,
+ EDITOR will be driven to describe differences between our
+ working copy and HEAD. */
+ SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx,
+ target_abspath,
+ &lock_fetch_reporter, &rb,
+ FALSE /* restore_files */,
+ depth, (! depth_as_sticky),
+ (! server_supports_depth),
+ FALSE /* use_commit_times */,
+ ctx->cancel_func, ctx->cancel_baton,
+ NULL, NULL, pool));
+ }
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify
+ = svn_wc_create_notify(target_abspath,
+ svn_wc_notify_status_completed, pool);
+ notify->revision = edit_revision;
+ (ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ }
+
+ /* If the caller wants the result revision, give it to them. */
+ if (result_rev)
+ *result_rev = edit_revision;
+ }
+ else
+ {
+ err = svn_wc_walk_status(ctx->wc_ctx, target_abspath,
+ depth, get_all, no_ignore, FALSE, ignores,
+ tweak_status, &sb,
+ ctx->cancel_func, ctx->cancel_baton,
+ pool);
+
+ if (err && err->apr_err == SVN_ERR_WC_MISSING)
+ {
+ /* This error code is checked for in svn to continue after
+ this error */
+ svn_error_clear(err);
+ return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+ _("'%s' is not a working copy"),
+ svn_dirent_local_style(path, pool));
+ }
+
+ SVN_ERR(err);
+ }
+
+ /* If there are svn:externals set, we don't want those to show up as
+ unversioned or unrecognized, so patch up the hash. If caller wants
+ all the statuses, we will change unversioned status items that
+ are interesting to an svn:externals property to
+ svn_wc_status_unversioned, otherwise we'll just remove the status
+ item altogether.
+
+ We only descend into an external if depth is svn_depth_infinity or
+ svn_depth_unknown. However, there are conceivable behaviors that
+ would involve descending under other circumstances; thus, we pass
+ depth anyway, so the code will DTRT if we change the conditional
+ in the future.
+ */
+ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
+ {
+ apr_hash_t *external_map;
+ SVN_ERR(svn_wc__externals_defined_below(&external_map,
+ ctx->wc_ctx, target_abspath,
+ pool, pool));
+
+
+ SVN_ERR(do_external_status(ctx, external_map,
+ depth, get_all,
+ update, no_ignore,
+ sb.anchor_abspath, sb.anchor_relpath,
+ status_func, status_baton, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_client_status_t *
+svn_client_status_dup(const svn_client_status_t *status,
+ apr_pool_t *result_pool)
+{
+ svn_client_status_t *st = apr_palloc(result_pool, sizeof(*st));
+
+ *st = *status;
+
+ if (status->local_abspath)
+ st->local_abspath = apr_pstrdup(result_pool, status->local_abspath);
+
+ if (status->repos_root_url)
+ st->repos_root_url = apr_pstrdup(result_pool, status->repos_root_url);
+
+ if (status->repos_uuid)
+ st->repos_uuid = apr_pstrdup(result_pool, status->repos_uuid);
+
+ if (status->repos_relpath)
+ st->repos_relpath = apr_pstrdup(result_pool, status->repos_relpath);
+
+ if (status->changed_author)
+ st->changed_author = apr_pstrdup(result_pool, status->changed_author);
+
+ if (status->lock)
+ st->lock = svn_lock_dup(status->lock, result_pool);
+
+ if (status->changelist)
+ st->changelist = apr_pstrdup(result_pool, status->changelist);
+
+ if (status->ood_changed_author)
+ st->ood_changed_author = apr_pstrdup(result_pool, status->ood_changed_author);
+
+ if (status->repos_lock)
+ st->repos_lock = svn_lock_dup(status->repos_lock, result_pool);
+
+ if (status->backwards_compatibility_baton)
+ {
+ const svn_wc_status3_t *wc_st = status->backwards_compatibility_baton;
+
+ st->backwards_compatibility_baton = svn_wc_dup_status3(wc_st,
+ result_pool);
+ }
+
+ if (status->moved_from_abspath)
+ st->moved_from_abspath =
+ apr_pstrdup(result_pool, status->moved_from_abspath);
+
+ if (status->moved_to_abspath)
+ st->moved_to_abspath = apr_pstrdup(result_pool, status->moved_to_abspath);
+
+ return st;
+}
+
+svn_error_t *
+svn_client__create_status(svn_client_status_t **cst,
+ svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ *cst = apr_pcalloc(result_pool, sizeof(**cst));
+
+ (*cst)->kind = status->kind;
+ (*cst)->local_abspath = local_abspath;
+ (*cst)->filesize = status->filesize;
+ (*cst)->versioned = status->versioned;
+
+ (*cst)->conflicted = status->conflicted;
+
+ (*cst)->node_status = status->node_status;
+ (*cst)->text_status = status->text_status;
+ (*cst)->prop_status = status->prop_status;
+
+ if (status->kind == svn_node_dir)
+ (*cst)->wc_is_locked = status->locked;
+
+ (*cst)->copied = status->copied;
+ (*cst)->revision = status->revision;
+
+ (*cst)->changed_rev = status->changed_rev;
+ (*cst)->changed_date = status->changed_date;
+ (*cst)->changed_author = status->changed_author;
+
+ (*cst)->repos_root_url = status->repos_root_url;
+ (*cst)->repos_uuid = status->repos_uuid;
+ (*cst)->repos_relpath = status->repos_relpath;
+
+ (*cst)->switched = status->switched;
+
+ (*cst)->file_external = status->file_external;
+ if (status->file_external)
+ {
+ (*cst)->switched = FALSE;
+ }
+
+ (*cst)->lock = status->lock;
+
+ (*cst)->changelist = status->changelist;
+ (*cst)->depth = status->depth;
+
+ /* Out of date information */
+ (*cst)->ood_kind = status->ood_kind;
+ (*cst)->repos_node_status = status->repos_node_status;
+ (*cst)->repos_text_status = status->repos_text_status;
+ (*cst)->repos_prop_status = status->repos_prop_status;
+ (*cst)->repos_lock = status->repos_lock;
+
+ (*cst)->ood_changed_rev = status->ood_changed_rev;
+ (*cst)->ood_changed_date = status->ood_changed_date;
+ (*cst)->ood_changed_author = status->ood_changed_author;
+
+ /* When changing the value of backwards_compatibility_baton, also
+ change its use in status4_wrapper_func in deprecated.c */
+ (*cst)->backwards_compatibility_baton = status;
+
+ if (status->versioned && status->conflicted)
+ {
+ svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
+
+ /* Note: This checks the on disk markers to automatically hide
+ text/property conflicts that are hidden by removing their
+ markers */
+ SVN_ERR(svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted,
+ &tree_conflicted, wc_ctx, local_abspath,
+ scratch_pool));
+
+ if (text_conflicted)
+ (*cst)->text_status = svn_wc_status_conflicted;
+
+ if (prop_conflicted)
+ (*cst)->prop_status = svn_wc_status_conflicted;
+
+ /* ### Also set this for tree_conflicts? */
+ if (text_conflicted || prop_conflicted)
+ (*cst)->node_status = svn_wc_status_conflicted;
+ }
+
+ (*cst)->moved_from_abspath = status->moved_from_abspath;
+ (*cst)->moved_to_abspath = status->moved_to_abspath;
+
+ return SVN_NO_ERROR;
+}
+