blob: 9e53713064d55b271d8abda51e27f05eaae1976e [file] [log] [blame]
/*
* 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.
* ====================================================================
*/
/* ==================================================================== */
/* We define this here to remove any further warnings about the usage of
experimental functions in this file. */
#define SVN_EXPERIMENTAL
/*** Includes. ***/
#include <apr_strings.h>
#include <apr_pools.h>
#include "svn_private_config.h"
#include "svn_pools.h"
#include "svn_sorts.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 "private/svn_client_shelf.h"
#include "private/svn_client_private.h"
#include "private/svn_sorts_private.h"
#include "private/svn_wc_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)
{
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 check_out_of_date,
svn_boolean_t check_working_copy,
svn_boolean_t no_ignore,
const apr_array_header_t *changelists,
const char *anchor_abspath,
const char *anchor_relpath,
svn_client_status_func_t status_func,
void *status_baton,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_array_header_t *externals;
int i;
externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically,
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 (i = 0; i < externals->nelts; i++)
{
svn_node_kind_t external_kind;
svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t);
const char *local_abspath = item.key;
const char *defining_abspath = item.value;
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_status6(NULL, ctx, status_path, &opt_rev, depth,
get_all, check_out_of_date,
check_working_copy, no_ignore,
FALSE /* ignore_exernals */,
FALSE /* depth_as_sticky */,
changelists, status_func, status_baton,
iterpool));
}
/* Destroy SUBPOOL and (implicitly) ITERPOOL. */
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Run status on shelf SHELF_NAME, if it exists.
*/
static svn_error_t *
shelf_status(const char *shelf_name,
const char *target_abspath,
svn_wc_status_func4_t status_func,
void *status_baton,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
svn_client__shelf_t *shelf;
svn_client__shelf_version_t *shelf_version;
const char *wc_relpath;
err = svn_client__shelf_open_existing(&shelf,
shelf_name, target_abspath,
ctx, scratch_pool);
if (err && err->apr_err == SVN_ERR_ILLEGAL_TARGET)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
else
SVN_ERR(err);
SVN_ERR(svn_client__shelf_version_open(&shelf_version,
shelf, shelf->max_version,
scratch_pool, scratch_pool));
wc_relpath = svn_dirent_skip_ancestor(shelf->wc_root_abspath, target_abspath);
SVN_ERR(svn_client__shelf_version_status_walk(shelf_version, wc_relpath,
status_func, status_baton,
scratch_pool));
SVN_ERR(svn_client__shelf_close(shelf, scratch_pool));
return SVN_NO_ERROR;
}
/* Run status on all shelves named in CHANGELISTS by a changelist name
* of the form "svn:shelf:SHELF_NAME", if they exist.
*/
static svn_error_t *
shelves_status(const apr_array_header_t *changelists,
const char *target_abspath,
svn_wc_status_func4_t status_func,
void *status_baton,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
static const char PREFIX[] = "svn:shelf:";
static const int PREFIX_LEN = 10;
int i;
if (! changelists)
return SVN_NO_ERROR;
for (i = 0; i < changelists->nelts; i++)
{
const char *cl = APR_ARRAY_IDX(changelists, i, const char *);
if (strncmp(cl, PREFIX, PREFIX_LEN) == 0)
{
const char *shelf_name = cl + PREFIX_LEN;
SVN_ERR(shelf_status(shelf_name, target_abspath,
status_func, status_baton,
ctx, scratch_pool));
}
}
return SVN_NO_ERROR;
}
/*** Public Interface. ***/
svn_error_t *
svn_client_status6(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 check_out_of_date,
svn_boolean_t check_working_copy,
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;
/* Override invalid combinations of the check_out_of_date and
check_working_copy flags. */
if (!check_out_of_date)
check_working_copy = TRUE;
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 (check_out_of_date)
{
/* 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 (check_out_of_date)
{
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, check_working_copy,
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
{
SVN_ERR(shelves_status(changelists, target_abspath,
tweak_status, &sb,
ctx, pool));
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);
}
/* 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,
check_out_of_date, check_working_copy,
no_ignore, changelists,
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;
}