1251881Speter/* 2251881Speter * status.c: return the status of a working copy dirent 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter#include <apr_strings.h> 30251881Speter#include <apr_pools.h> 31251881Speter 32299742Sdim#include "svn_private_config.h" 33251881Speter#include "svn_pools.h" 34299742Sdim#include "svn_sorts.h" 35251881Speter#include "client.h" 36251881Speter 37251881Speter#include "svn_path.h" 38251881Speter#include "svn_dirent_uri.h" 39251881Speter#include "svn_delta.h" 40251881Speter#include "svn_client.h" 41251881Speter#include "svn_error.h" 42251881Speter#include "svn_hash.h" 43251881Speter 44299742Sdim#include "private/svn_client_private.h" 45299742Sdim#include "private/svn_sorts_private.h" 46251881Speter#include "private/svn_wc_private.h" 47251881Speter 48251881Speter 49251881Speter/*** Getting update information ***/ 50251881Speter 51251881Speter/* Baton for tweak_status. It wraps a bit of extra functionality 52251881Speter around the received status func/baton, so we can remember if the 53251881Speter target was deleted in HEAD and tweak incoming status structures 54251881Speter accordingly. */ 55251881Speterstruct status_baton 56251881Speter{ 57251881Speter svn_boolean_t deleted_in_repos; /* target is deleted in repos */ 58251881Speter apr_hash_t *changelist_hash; /* keys are changelist names */ 59251881Speter svn_client_status_func_t real_status_func; /* real status function */ 60251881Speter void *real_status_baton; /* real status baton */ 61251881Speter const char *anchor_abspath; /* Absolute path of anchor */ 62251881Speter const char *anchor_relpath; /* Relative path of anchor */ 63251881Speter svn_wc_context_t *wc_ctx; /* A working copy context. */ 64251881Speter}; 65251881Speter 66251881Speter/* A status callback function which wraps the *real* status 67251881Speter function/baton. This sucker takes care of any status tweaks we 68251881Speter need to make (such as noting that the target of the status is 69251881Speter missing from HEAD in the repository). 70251881Speter 71251881Speter This implements the 'svn_wc_status_func4_t' function type. */ 72251881Speterstatic svn_error_t * 73251881Spetertweak_status(void *baton, 74251881Speter const char *local_abspath, 75251881Speter const svn_wc_status3_t *status, 76251881Speter apr_pool_t *scratch_pool) 77251881Speter{ 78251881Speter struct status_baton *sb = baton; 79251881Speter const char *path = local_abspath; 80251881Speter svn_client_status_t *cst; 81251881Speter 82251881Speter if (sb->anchor_abspath) 83251881Speter path = svn_dirent_join(sb->anchor_relpath, 84251881Speter svn_dirent_skip_ancestor(sb->anchor_abspath, path), 85251881Speter scratch_pool); 86251881Speter 87251881Speter /* If the status item has an entry, but doesn't belong to one of the 88251881Speter changelists our caller is interested in, we filter out this status 89251881Speter transmission. */ 90251881Speter if (sb->changelist_hash 91251881Speter && (! status->changelist 92251881Speter || ! svn_hash_gets(sb->changelist_hash, status->changelist))) 93251881Speter { 94251881Speter return SVN_NO_ERROR; 95251881Speter } 96251881Speter 97251881Speter /* If we know that the target was deleted in HEAD of the repository, 98251881Speter we need to note that fact in all the status structures that come 99251881Speter through here. */ 100251881Speter if (sb->deleted_in_repos) 101251881Speter { 102251881Speter svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool); 103251881Speter new_status->repos_node_status = svn_wc_status_deleted; 104251881Speter status = new_status; 105251881Speter } 106251881Speter 107251881Speter SVN_ERR(svn_client__create_status(&cst, sb->wc_ctx, local_abspath, status, 108251881Speter scratch_pool, scratch_pool)); 109251881Speter 110251881Speter /* Call the real status function/baton. */ 111251881Speter return sb->real_status_func(sb->real_status_baton, path, cst, 112251881Speter scratch_pool); 113251881Speter} 114251881Speter 115251881Speter/* A baton for our reporter that is used to collect locks. */ 116251881Spetertypedef struct report_baton_t { 117251881Speter const svn_ra_reporter3_t* wrapped_reporter; 118251881Speter void *wrapped_report_baton; 119251881Speter /* The common ancestor URL of all paths included in the report. */ 120251881Speter char *ancestor; 121251881Speter void *set_locks_baton; 122251881Speter svn_depth_t depth; 123251881Speter svn_client_ctx_t *ctx; 124251881Speter /* Pool to store locks in. */ 125251881Speter apr_pool_t *pool; 126251881Speter} report_baton_t; 127251881Speter 128251881Speter/* Implements svn_ra_reporter3_t->set_path. */ 129251881Speterstatic svn_error_t * 130251881Speterreporter_set_path(void *report_baton, const char *path, 131251881Speter svn_revnum_t revision, svn_depth_t depth, 132251881Speter svn_boolean_t start_empty, const char *lock_token, 133251881Speter apr_pool_t *pool) 134251881Speter{ 135251881Speter report_baton_t *rb = report_baton; 136251881Speter 137251881Speter return rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path, 138251881Speter revision, depth, start_empty, 139251881Speter lock_token, pool); 140251881Speter} 141251881Speter 142251881Speter/* Implements svn_ra_reporter3_t->delete_path. */ 143251881Speterstatic svn_error_t * 144251881Speterreporter_delete_path(void *report_baton, const char *path, apr_pool_t *pool) 145251881Speter{ 146251881Speter report_baton_t *rb = report_baton; 147251881Speter 148251881Speter return rb->wrapped_reporter->delete_path(rb->wrapped_report_baton, path, 149251881Speter pool); 150251881Speter} 151251881Speter 152251881Speter/* Implements svn_ra_reporter3_t->link_path. */ 153251881Speterstatic svn_error_t * 154251881Speterreporter_link_path(void *report_baton, const char *path, const char *url, 155251881Speter svn_revnum_t revision, svn_depth_t depth, 156251881Speter svn_boolean_t start_empty, 157251881Speter const char *lock_token, apr_pool_t *pool) 158251881Speter{ 159251881Speter report_baton_t *rb = report_baton; 160251881Speter 161251881Speter if (!svn_uri__is_ancestor(rb->ancestor, url)) 162251881Speter { 163251881Speter const char *ancestor; 164251881Speter 165251881Speter ancestor = svn_uri_get_longest_ancestor(url, rb->ancestor, pool); 166251881Speter 167251881Speter /* If we got a shorter ancestor, truncate our current ancestor. 168251881Speter Note that svn_uri_get_longest_ancestor will allocate its return 169251881Speter value even if it identical to one of its arguments. */ 170251881Speter 171251881Speter rb->ancestor[strlen(ancestor)] = '\0'; 172251881Speter rb->depth = svn_depth_infinity; 173251881Speter } 174251881Speter 175251881Speter return rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url, 176251881Speter revision, depth, start_empty, 177251881Speter lock_token, pool); 178251881Speter} 179251881Speter 180251881Speter/* Implements svn_ra_reporter3_t->finish_report. */ 181251881Speterstatic svn_error_t * 182251881Speterreporter_finish_report(void *report_baton, apr_pool_t *pool) 183251881Speter{ 184251881Speter report_baton_t *rb = report_baton; 185251881Speter svn_ra_session_t *ras; 186251881Speter apr_hash_t *locks; 187251881Speter const char *repos_root; 188251881Speter apr_pool_t *subpool = svn_pool_create(pool); 189251881Speter svn_error_t *err = SVN_NO_ERROR; 190251881Speter 191251881Speter /* Open an RA session to our common ancestor and grab the locks under it. 192251881Speter */ 193251881Speter SVN_ERR(svn_client_open_ra_session2(&ras, rb->ancestor, NULL, 194251881Speter rb->ctx, subpool, subpool)); 195251881Speter 196251881Speter /* The locks need to live throughout the edit. Note that if the 197251881Speter server doesn't support lock discovery, we'll just not do locky 198251881Speter stuff. */ 199251881Speter err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool); 200299742Sdim if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) 201251881Speter { 202251881Speter svn_error_clear(err); 203251881Speter err = SVN_NO_ERROR; 204251881Speter locks = apr_hash_make(rb->pool); 205251881Speter } 206251881Speter SVN_ERR(err); 207251881Speter 208251881Speter SVN_ERR(svn_ra_get_repos_root2(ras, &repos_root, rb->pool)); 209251881Speter 210251881Speter /* Close the RA session. */ 211251881Speter svn_pool_destroy(subpool); 212251881Speter 213251881Speter SVN_ERR(svn_wc_status_set_repos_locks(rb->set_locks_baton, locks, 214251881Speter repos_root, rb->pool)); 215251881Speter 216251881Speter return rb->wrapped_reporter->finish_report(rb->wrapped_report_baton, pool); 217251881Speter} 218251881Speter 219251881Speter/* Implements svn_ra_reporter3_t->abort_report. */ 220251881Speterstatic svn_error_t * 221251881Speterreporter_abort_report(void *report_baton, apr_pool_t *pool) 222251881Speter{ 223251881Speter report_baton_t *rb = report_baton; 224251881Speter 225251881Speter return rb->wrapped_reporter->abort_report(rb->wrapped_report_baton, pool); 226251881Speter} 227251881Speter 228251881Speter/* A reporter that keeps track of the common URL ancestor of all paths in 229251881Speter the WC and fetches repository locks for all paths under this ancestor. */ 230251881Speterstatic svn_ra_reporter3_t lock_fetch_reporter = { 231251881Speter reporter_set_path, 232251881Speter reporter_delete_path, 233251881Speter reporter_link_path, 234251881Speter reporter_finish_report, 235251881Speter reporter_abort_report 236251881Speter}; 237251881Speter 238251881Speter/* Perform status operations on each external in EXTERNAL_MAP, a const char * 239251881Speter local_abspath of all externals mapping to the const char* defining_abspath. 240251881Speter All other options are the same as those passed to svn_client_status(). 241251881Speter 242251881Speter If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide 243251881Speter properly formatted relative paths */ 244251881Speterstatic svn_error_t * 245251881Speterdo_external_status(svn_client_ctx_t *ctx, 246251881Speter apr_hash_t *external_map, 247251881Speter svn_depth_t depth, 248251881Speter svn_boolean_t get_all, 249299742Sdim svn_boolean_t check_out_of_date, 250299742Sdim svn_boolean_t check_working_copy, 251251881Speter svn_boolean_t no_ignore, 252299742Sdim const apr_array_header_t *changelists, 253251881Speter const char *anchor_abspath, 254251881Speter const char *anchor_relpath, 255251881Speter svn_client_status_func_t status_func, 256251881Speter void *status_baton, 257251881Speter apr_pool_t *scratch_pool) 258251881Speter{ 259251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 260299742Sdim apr_array_header_t *externals; 261299742Sdim int i; 262251881Speter 263299742Sdim externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically, 264299742Sdim scratch_pool); 265299742Sdim 266251881Speter /* Loop over the hash of new values (we don't care about the old 267251881Speter ones). This is a mapping of versioned directories to property 268251881Speter values. */ 269299742Sdim for (i = 0; i < externals->nelts; i++) 270251881Speter { 271251881Speter svn_node_kind_t external_kind; 272299742Sdim svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t); 273299742Sdim const char *local_abspath = item.key; 274299742Sdim const char *defining_abspath = item.value; 275251881Speter svn_node_kind_t kind; 276251881Speter svn_opt_revision_t opt_rev; 277251881Speter const char *status_path; 278251881Speter 279251881Speter svn_pool_clear(iterpool); 280251881Speter 281251881Speter /* Obtain information on the expected external. */ 282251881Speter SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, 283251881Speter &opt_rev.value.number, 284251881Speter ctx->wc_ctx, defining_abspath, 285251881Speter local_abspath, FALSE, 286251881Speter iterpool, iterpool)); 287251881Speter 288251881Speter if (external_kind != svn_node_dir) 289251881Speter continue; 290251881Speter 291251881Speter SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool)); 292251881Speter if (kind != svn_node_dir) 293251881Speter continue; 294251881Speter 295251881Speter if (SVN_IS_VALID_REVNUM(opt_rev.value.number)) 296251881Speter opt_rev.kind = svn_opt_revision_number; 297251881Speter else 298251881Speter opt_rev.kind = svn_opt_revision_unspecified; 299251881Speter 300251881Speter /* Tell the client we're starting an external status set. */ 301251881Speter if (ctx->notify_func2) 302251881Speter ctx->notify_func2( 303251881Speter ctx->notify_baton2, 304251881Speter svn_wc_create_notify(local_abspath, 305251881Speter svn_wc_notify_status_external, 306251881Speter iterpool), iterpool); 307251881Speter 308251881Speter status_path = local_abspath; 309251881Speter if (anchor_abspath) 310251881Speter { 311251881Speter status_path = svn_dirent_join(anchor_relpath, 312251881Speter svn_dirent_skip_ancestor(anchor_abspath, 313251881Speter status_path), 314251881Speter iterpool); 315251881Speter } 316251881Speter 317251881Speter /* And then do the status. */ 318299742Sdim SVN_ERR(svn_client_status6(NULL, ctx, status_path, &opt_rev, depth, 319299742Sdim get_all, check_out_of_date, 320299742Sdim check_working_copy, no_ignore, 321299742Sdim FALSE /* ignore_exernals */, 322299742Sdim FALSE /* depth_as_sticky */, 323299742Sdim changelists, status_func, status_baton, 324251881Speter iterpool)); 325251881Speter } 326251881Speter 327251881Speter /* Destroy SUBPOOL and (implicitly) ITERPOOL. */ 328251881Speter svn_pool_destroy(iterpool); 329251881Speter 330251881Speter return SVN_NO_ERROR; 331251881Speter} 332251881Speter 333251881Speter/*** Public Interface. ***/ 334251881Speter 335251881Speter 336251881Spetersvn_error_t * 337299742Sdimsvn_client_status6(svn_revnum_t *result_rev, 338251881Speter svn_client_ctx_t *ctx, 339251881Speter const char *path, 340251881Speter const svn_opt_revision_t *revision, 341251881Speter svn_depth_t depth, 342251881Speter svn_boolean_t get_all, 343299742Sdim svn_boolean_t check_out_of_date, 344299742Sdim svn_boolean_t check_working_copy, 345251881Speter svn_boolean_t no_ignore, 346251881Speter svn_boolean_t ignore_externals, 347251881Speter svn_boolean_t depth_as_sticky, 348251881Speter const apr_array_header_t *changelists, 349251881Speter svn_client_status_func_t status_func, 350251881Speter void *status_baton, 351251881Speter apr_pool_t *pool) /* ### aka scratch_pool */ 352251881Speter{ 353251881Speter struct status_baton sb; 354251881Speter const char *dir, *dir_abspath; 355251881Speter const char *target_abspath; 356251881Speter const char *target_basename; 357251881Speter apr_array_header_t *ignores; 358251881Speter svn_error_t *err; 359251881Speter apr_hash_t *changelist_hash = NULL; 360251881Speter 361299742Sdim /* Override invalid combinations of the check_out_of_date and 362299742Sdim check_working_copy flags. */ 363299742Sdim if (!check_out_of_date) 364299742Sdim check_working_copy = TRUE; 365299742Sdim 366251881Speter if (svn_path_is_url(path)) 367251881Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 368251881Speter _("'%s' is not a local path"), path); 369251881Speter 370251881Speter if (changelists && changelists->nelts) 371251881Speter SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool)); 372251881Speter 373251881Speter if (result_rev) 374251881Speter *result_rev = SVN_INVALID_REVNUM; 375251881Speter 376251881Speter sb.real_status_func = status_func; 377251881Speter sb.real_status_baton = status_baton; 378251881Speter sb.deleted_in_repos = FALSE; 379251881Speter sb.changelist_hash = changelist_hash; 380251881Speter sb.wc_ctx = ctx->wc_ctx; 381251881Speter 382251881Speter SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool)); 383251881Speter 384299742Sdim if (check_out_of_date) 385251881Speter { 386251881Speter /* The status editor only works on directories, so get the ancestor 387251881Speter if necessary */ 388251881Speter 389251881Speter svn_node_kind_t kind; 390251881Speter 391251881Speter SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath, 392251881Speter TRUE, FALSE, pool)); 393251881Speter 394251881Speter /* Dir must be a working copy directory or the status editor fails */ 395251881Speter if (kind == svn_node_dir) 396251881Speter { 397251881Speter dir_abspath = target_abspath; 398251881Speter target_basename = ""; 399251881Speter dir = path; 400251881Speter } 401251881Speter else 402251881Speter { 403251881Speter dir_abspath = svn_dirent_dirname(target_abspath, pool); 404251881Speter target_basename = svn_dirent_basename(target_abspath, NULL); 405251881Speter dir = svn_dirent_dirname(path, pool); 406251881Speter 407251881Speter if (kind == svn_node_file) 408251881Speter { 409251881Speter if (depth == svn_depth_empty) 410251881Speter depth = svn_depth_files; 411251881Speter } 412251881Speter else 413251881Speter { 414251881Speter err = svn_wc_read_kind2(&kind, ctx->wc_ctx, dir_abspath, 415251881Speter FALSE, FALSE, pool); 416251881Speter 417251881Speter svn_error_clear(err); 418251881Speter 419251881Speter if (err || kind != svn_node_dir) 420251881Speter { 421251881Speter return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 422251881Speter _("'%s' is not a working copy"), 423251881Speter svn_dirent_local_style(path, pool)); 424251881Speter } 425251881Speter } 426251881Speter } 427251881Speter } 428251881Speter else 429251881Speter { 430251881Speter dir = path; 431251881Speter dir_abspath = target_abspath; 432251881Speter } 433251881Speter 434251881Speter if (svn_dirent_is_absolute(dir)) 435251881Speter { 436251881Speter sb.anchor_abspath = NULL; 437251881Speter sb.anchor_relpath = NULL; 438251881Speter } 439251881Speter else 440251881Speter { 441251881Speter sb.anchor_abspath = dir_abspath; 442251881Speter sb.anchor_relpath = dir; 443251881Speter } 444251881Speter 445251881Speter /* Get the status edit, and use our wrapping status function/baton 446251881Speter as the callback pair. */ 447251881Speter SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool)); 448251881Speter 449251881Speter /* If we want to know about out-of-dateness, we crawl the working copy and 450251881Speter let the RA layer drive the editor for real. Otherwise, we just close the 451251881Speter edit. :-) */ 452299742Sdim if (check_out_of_date) 453251881Speter { 454251881Speter svn_ra_session_t *ra_session; 455251881Speter const char *URL; 456251881Speter svn_node_kind_t kind; 457251881Speter svn_boolean_t server_supports_depth; 458251881Speter const svn_delta_editor_t *editor; 459251881Speter void *edit_baton, *set_locks_baton; 460251881Speter svn_revnum_t edit_revision = SVN_INVALID_REVNUM; 461251881Speter 462251881Speter /* Get full URL from the ANCHOR. */ 463251881Speter SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx, 464251881Speter pool, pool)); 465251881Speter 466251881Speter if (!URL) 467251881Speter return svn_error_createf 468251881Speter (SVN_ERR_ENTRY_MISSING_URL, NULL, 469251881Speter _("Entry '%s' has no URL"), 470251881Speter svn_dirent_local_style(dir, pool)); 471251881Speter 472251881Speter /* Open a repository session to the URL. */ 473251881Speter SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL, 474251881Speter dir_abspath, NULL, 475251881Speter FALSE, TRUE, 476251881Speter ctx, pool, pool)); 477251881Speter 478251881Speter SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, 479251881Speter SVN_RA_CAPABILITY_DEPTH, pool)); 480251881Speter 481251881Speter SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton, 482299742Sdim &edit_revision, ctx->wc_ctx, 483299742Sdim dir_abspath, target_basename, 484299742Sdim depth, get_all, check_working_copy, 485299742Sdim no_ignore, depth_as_sticky, 486299742Sdim server_supports_depth, 487299742Sdim ignores, tweak_status, &sb, 488299742Sdim ctx->cancel_func, ctx->cancel_baton, 489299742Sdim pool, pool)); 490251881Speter 491251881Speter 492251881Speter /* Verify that URL exists in HEAD. If it doesn't, this can save 493251881Speter us a whole lot of hassle; if it does, the cost of this 494251881Speter request should be minimal compared to the size of getting 495251881Speter back the average amount of "out-of-date" information. */ 496251881Speter SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, 497251881Speter &kind, pool)); 498251881Speter if (kind == svn_node_none) 499251881Speter { 500251881Speter svn_boolean_t added; 501251881Speter 502251881Speter /* Our status target does not exist in HEAD. If we've got 503251881Speter it locally added, that's okay. But if it was previously 504251881Speter versioned, then it must have since been deleted from the 505251881Speter repository. (Note that "locally replaced" doesn't count 506251881Speter as "added" in this case.) */ 507251881Speter SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx, 508251881Speter dir_abspath, pool)); 509251881Speter if (! added) 510251881Speter sb.deleted_in_repos = TRUE; 511251881Speter 512251881Speter /* And now close the edit. */ 513251881Speter SVN_ERR(editor->close_edit(edit_baton, pool)); 514251881Speter } 515251881Speter else 516251881Speter { 517251881Speter svn_revnum_t revnum; 518251881Speter report_baton_t rb; 519251881Speter svn_depth_t status_depth; 520251881Speter 521251881Speter if (revision->kind == svn_opt_revision_head) 522251881Speter { 523251881Speter /* Cause the revision number to be omitted from the request, 524251881Speter which implies HEAD. */ 525251881Speter revnum = SVN_INVALID_REVNUM; 526251881Speter } 527251881Speter else 528251881Speter { 529251881Speter /* Get a revision number for our status operation. */ 530251881Speter SVN_ERR(svn_client__get_revision_number(&revnum, NULL, 531251881Speter ctx->wc_ctx, 532251881Speter target_abspath, 533251881Speter ra_session, revision, 534251881Speter pool)); 535251881Speter } 536251881Speter 537251881Speter if (depth_as_sticky || !server_supports_depth) 538251881Speter status_depth = depth; 539251881Speter else 540251881Speter status_depth = svn_depth_unknown; /* Use depth from WC */ 541251881Speter 542251881Speter /* Do the deed. Let the RA layer drive the status editor. */ 543251881Speter SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter, 544251881Speter &rb.wrapped_report_baton, 545251881Speter target_basename, revnum, status_depth, 546251881Speter editor, edit_baton, pool)); 547251881Speter 548251881Speter /* Init the report baton. */ 549251881Speter rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */ 550251881Speter rb.set_locks_baton = set_locks_baton; 551251881Speter rb.ctx = ctx; 552251881Speter rb.pool = pool; 553251881Speter 554251881Speter if (depth == svn_depth_unknown) 555251881Speter rb.depth = svn_depth_infinity; 556251881Speter else 557251881Speter rb.depth = depth; 558251881Speter 559251881Speter /* Drive the reporter structure, describing the revisions 560251881Speter within PATH. When we call reporter->finish_report, 561251881Speter EDITOR will be driven to describe differences between our 562251881Speter working copy and HEAD. */ 563251881Speter SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, 564251881Speter target_abspath, 565251881Speter &lock_fetch_reporter, &rb, 566251881Speter FALSE /* restore_files */, 567251881Speter depth, (! depth_as_sticky), 568251881Speter (! server_supports_depth), 569251881Speter FALSE /* use_commit_times */, 570251881Speter ctx->cancel_func, ctx->cancel_baton, 571251881Speter NULL, NULL, pool)); 572251881Speter } 573251881Speter 574251881Speter if (ctx->notify_func2) 575251881Speter { 576251881Speter svn_wc_notify_t *notify 577251881Speter = svn_wc_create_notify(target_abspath, 578251881Speter svn_wc_notify_status_completed, pool); 579251881Speter notify->revision = edit_revision; 580299742Sdim ctx->notify_func2(ctx->notify_baton2, notify, pool); 581251881Speter } 582251881Speter 583251881Speter /* If the caller wants the result revision, give it to them. */ 584251881Speter if (result_rev) 585251881Speter *result_rev = edit_revision; 586251881Speter } 587251881Speter else 588251881Speter { 589251881Speter err = svn_wc_walk_status(ctx->wc_ctx, target_abspath, 590251881Speter depth, get_all, no_ignore, FALSE, ignores, 591251881Speter tweak_status, &sb, 592251881Speter ctx->cancel_func, ctx->cancel_baton, 593251881Speter pool); 594251881Speter 595251881Speter if (err && err->apr_err == SVN_ERR_WC_MISSING) 596251881Speter { 597251881Speter /* This error code is checked for in svn to continue after 598251881Speter this error */ 599251881Speter svn_error_clear(err); 600251881Speter return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 601251881Speter _("'%s' is not a working copy"), 602251881Speter svn_dirent_local_style(path, pool)); 603251881Speter } 604251881Speter 605251881Speter SVN_ERR(err); 606251881Speter } 607251881Speter 608299742Sdim /* We only descend into an external if depth is svn_depth_infinity or 609251881Speter svn_depth_unknown. However, there are conceivable behaviors that 610251881Speter would involve descending under other circumstances; thus, we pass 611251881Speter depth anyway, so the code will DTRT if we change the conditional 612251881Speter in the future. 613251881Speter */ 614251881Speter if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) 615251881Speter { 616251881Speter apr_hash_t *external_map; 617251881Speter SVN_ERR(svn_wc__externals_defined_below(&external_map, 618251881Speter ctx->wc_ctx, target_abspath, 619251881Speter pool, pool)); 620251881Speter 621251881Speter 622251881Speter SVN_ERR(do_external_status(ctx, external_map, 623251881Speter depth, get_all, 624299742Sdim check_out_of_date, check_working_copy, 625299742Sdim no_ignore, changelists, 626251881Speter sb.anchor_abspath, sb.anchor_relpath, 627251881Speter status_func, status_baton, pool)); 628251881Speter } 629251881Speter 630251881Speter return SVN_NO_ERROR; 631251881Speter} 632251881Speter 633251881Spetersvn_client_status_t * 634251881Spetersvn_client_status_dup(const svn_client_status_t *status, 635251881Speter apr_pool_t *result_pool) 636251881Speter{ 637251881Speter svn_client_status_t *st = apr_palloc(result_pool, sizeof(*st)); 638251881Speter 639251881Speter *st = *status; 640251881Speter 641251881Speter if (status->local_abspath) 642251881Speter st->local_abspath = apr_pstrdup(result_pool, status->local_abspath); 643251881Speter 644251881Speter if (status->repos_root_url) 645251881Speter st->repos_root_url = apr_pstrdup(result_pool, status->repos_root_url); 646251881Speter 647251881Speter if (status->repos_uuid) 648251881Speter st->repos_uuid = apr_pstrdup(result_pool, status->repos_uuid); 649251881Speter 650251881Speter if (status->repos_relpath) 651251881Speter st->repos_relpath = apr_pstrdup(result_pool, status->repos_relpath); 652251881Speter 653251881Speter if (status->changed_author) 654251881Speter st->changed_author = apr_pstrdup(result_pool, status->changed_author); 655251881Speter 656251881Speter if (status->lock) 657251881Speter st->lock = svn_lock_dup(status->lock, result_pool); 658251881Speter 659251881Speter if (status->changelist) 660251881Speter st->changelist = apr_pstrdup(result_pool, status->changelist); 661251881Speter 662251881Speter if (status->ood_changed_author) 663251881Speter st->ood_changed_author = apr_pstrdup(result_pool, status->ood_changed_author); 664251881Speter 665251881Speter if (status->repos_lock) 666251881Speter st->repos_lock = svn_lock_dup(status->repos_lock, result_pool); 667251881Speter 668251881Speter if (status->backwards_compatibility_baton) 669251881Speter { 670251881Speter const svn_wc_status3_t *wc_st = status->backwards_compatibility_baton; 671251881Speter 672251881Speter st->backwards_compatibility_baton = svn_wc_dup_status3(wc_st, 673251881Speter result_pool); 674251881Speter } 675251881Speter 676251881Speter if (status->moved_from_abspath) 677251881Speter st->moved_from_abspath = 678251881Speter apr_pstrdup(result_pool, status->moved_from_abspath); 679251881Speter 680251881Speter if (status->moved_to_abspath) 681251881Speter st->moved_to_abspath = apr_pstrdup(result_pool, status->moved_to_abspath); 682251881Speter 683251881Speter return st; 684251881Speter} 685251881Speter 686251881Spetersvn_error_t * 687251881Spetersvn_client__create_status(svn_client_status_t **cst, 688251881Speter svn_wc_context_t *wc_ctx, 689251881Speter const char *local_abspath, 690251881Speter const svn_wc_status3_t *status, 691251881Speter apr_pool_t *result_pool, 692251881Speter apr_pool_t *scratch_pool) 693251881Speter{ 694251881Speter *cst = apr_pcalloc(result_pool, sizeof(**cst)); 695251881Speter 696251881Speter (*cst)->kind = status->kind; 697251881Speter (*cst)->local_abspath = local_abspath; 698251881Speter (*cst)->filesize = status->filesize; 699251881Speter (*cst)->versioned = status->versioned; 700251881Speter 701251881Speter (*cst)->conflicted = status->conflicted; 702251881Speter 703251881Speter (*cst)->node_status = status->node_status; 704251881Speter (*cst)->text_status = status->text_status; 705251881Speter (*cst)->prop_status = status->prop_status; 706251881Speter 707251881Speter if (status->kind == svn_node_dir) 708251881Speter (*cst)->wc_is_locked = status->locked; 709251881Speter 710251881Speter (*cst)->copied = status->copied; 711251881Speter (*cst)->revision = status->revision; 712251881Speter 713251881Speter (*cst)->changed_rev = status->changed_rev; 714251881Speter (*cst)->changed_date = status->changed_date; 715251881Speter (*cst)->changed_author = status->changed_author; 716251881Speter 717251881Speter (*cst)->repos_root_url = status->repos_root_url; 718251881Speter (*cst)->repos_uuid = status->repos_uuid; 719251881Speter (*cst)->repos_relpath = status->repos_relpath; 720251881Speter 721251881Speter (*cst)->switched = status->switched; 722251881Speter 723251881Speter (*cst)->file_external = status->file_external; 724251881Speter if (status->file_external) 725251881Speter { 726251881Speter (*cst)->switched = FALSE; 727251881Speter } 728251881Speter 729251881Speter (*cst)->lock = status->lock; 730251881Speter 731251881Speter (*cst)->changelist = status->changelist; 732251881Speter (*cst)->depth = status->depth; 733251881Speter 734251881Speter /* Out of date information */ 735251881Speter (*cst)->ood_kind = status->ood_kind; 736251881Speter (*cst)->repos_node_status = status->repos_node_status; 737251881Speter (*cst)->repos_text_status = status->repos_text_status; 738251881Speter (*cst)->repos_prop_status = status->repos_prop_status; 739251881Speter (*cst)->repos_lock = status->repos_lock; 740251881Speter 741251881Speter (*cst)->ood_changed_rev = status->ood_changed_rev; 742251881Speter (*cst)->ood_changed_date = status->ood_changed_date; 743251881Speter (*cst)->ood_changed_author = status->ood_changed_author; 744251881Speter 745251881Speter /* When changing the value of backwards_compatibility_baton, also 746251881Speter change its use in status4_wrapper_func in deprecated.c */ 747251881Speter (*cst)->backwards_compatibility_baton = status; 748251881Speter 749251881Speter if (status->versioned && status->conflicted) 750251881Speter { 751251881Speter svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted; 752251881Speter 753251881Speter /* Note: This checks the on disk markers to automatically hide 754251881Speter text/property conflicts that are hidden by removing their 755251881Speter markers */ 756251881Speter SVN_ERR(svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted, 757251881Speter &tree_conflicted, wc_ctx, local_abspath, 758251881Speter scratch_pool)); 759251881Speter 760251881Speter if (text_conflicted) 761251881Speter (*cst)->text_status = svn_wc_status_conflicted; 762251881Speter 763251881Speter if (prop_conflicted) 764251881Speter (*cst)->prop_status = svn_wc_status_conflicted; 765251881Speter 766251881Speter /* ### Also set this for tree_conflicts? */ 767251881Speter if (text_conflicted || prop_conflicted) 768251881Speter (*cst)->node_status = svn_wc_status_conflicted; 769251881Speter } 770251881Speter 771251881Speter (*cst)->moved_from_abspath = status->moved_from_abspath; 772251881Speter (*cst)->moved_to_abspath = status->moved_to_abspath; 773251881Speter 774251881Speter return SVN_NO_ERROR; 775251881Speter} 776251881Speter 777