1/* 2 * status.c: return the status of a working copy dirent 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* ==================================================================== */ 25 26 27 28/*** Includes. ***/ 29#include <apr_strings.h> 30#include <apr_pools.h> 31 32#include "svn_pools.h" 33#include "client.h" 34 35#include "svn_path.h" 36#include "svn_dirent_uri.h" 37#include "svn_delta.h" 38#include "svn_client.h" 39#include "svn_error.h" 40#include "svn_hash.h" 41 42#include "svn_private_config.h" 43#include "private/svn_wc_private.h" 44#include "private/svn_client_private.h" 45 46 47/*** Getting update information ***/ 48 49/* Baton for tweak_status. It wraps a bit of extra functionality 50 around the received status func/baton, so we can remember if the 51 target was deleted in HEAD and tweak incoming status structures 52 accordingly. */ 53struct status_baton 54{ 55 svn_boolean_t deleted_in_repos; /* target is deleted in repos */ 56 apr_hash_t *changelist_hash; /* keys are changelist names */ 57 svn_client_status_func_t real_status_func; /* real status function */ 58 void *real_status_baton; /* real status baton */ 59 const char *anchor_abspath; /* Absolute path of anchor */ 60 const char *anchor_relpath; /* Relative path of anchor */ 61 svn_wc_context_t *wc_ctx; /* A working copy context. */ 62}; 63 64/* A status callback function which wraps the *real* status 65 function/baton. This sucker takes care of any status tweaks we 66 need to make (such as noting that the target of the status is 67 missing from HEAD in the repository). 68 69 This implements the 'svn_wc_status_func4_t' function type. */ 70static svn_error_t * 71tweak_status(void *baton, 72 const char *local_abspath, 73 const svn_wc_status3_t *status, 74 apr_pool_t *scratch_pool) 75{ 76 struct status_baton *sb = baton; 77 const char *path = local_abspath; 78 svn_client_status_t *cst; 79 80 if (sb->anchor_abspath) 81 path = svn_dirent_join(sb->anchor_relpath, 82 svn_dirent_skip_ancestor(sb->anchor_abspath, path), 83 scratch_pool); 84 85 /* If the status item has an entry, but doesn't belong to one of the 86 changelists our caller is interested in, we filter out this status 87 transmission. */ 88 if (sb->changelist_hash 89 && (! status->changelist 90 || ! svn_hash_gets(sb->changelist_hash, status->changelist))) 91 { 92 return SVN_NO_ERROR; 93 } 94 95 /* If we know that the target was deleted in HEAD of the repository, 96 we need to note that fact in all the status structures that come 97 through here. */ 98 if (sb->deleted_in_repos) 99 { 100 svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool); 101 new_status->repos_node_status = svn_wc_status_deleted; 102 status = new_status; 103 } 104 105 SVN_ERR(svn_client__create_status(&cst, sb->wc_ctx, local_abspath, status, 106 scratch_pool, scratch_pool)); 107 108 /* Call the real status function/baton. */ 109 return sb->real_status_func(sb->real_status_baton, path, cst, 110 scratch_pool); 111} 112 113/* A baton for our reporter that is used to collect locks. */ 114typedef struct report_baton_t { 115 const svn_ra_reporter3_t* wrapped_reporter; 116 void *wrapped_report_baton; 117 /* The common ancestor URL of all paths included in the report. */ 118 char *ancestor; 119 void *set_locks_baton; 120 svn_depth_t depth; 121 svn_client_ctx_t *ctx; 122 /* Pool to store locks in. */ 123 apr_pool_t *pool; 124} report_baton_t; 125 126/* Implements svn_ra_reporter3_t->set_path. */ 127static svn_error_t * 128reporter_set_path(void *report_baton, const char *path, 129 svn_revnum_t revision, svn_depth_t depth, 130 svn_boolean_t start_empty, const char *lock_token, 131 apr_pool_t *pool) 132{ 133 report_baton_t *rb = report_baton; 134 135 return rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path, 136 revision, depth, start_empty, 137 lock_token, pool); 138} 139 140/* Implements svn_ra_reporter3_t->delete_path. */ 141static svn_error_t * 142reporter_delete_path(void *report_baton, const char *path, apr_pool_t *pool) 143{ 144 report_baton_t *rb = report_baton; 145 146 return rb->wrapped_reporter->delete_path(rb->wrapped_report_baton, path, 147 pool); 148} 149 150/* Implements svn_ra_reporter3_t->link_path. */ 151static svn_error_t * 152reporter_link_path(void *report_baton, const char *path, const char *url, 153 svn_revnum_t revision, svn_depth_t depth, 154 svn_boolean_t start_empty, 155 const char *lock_token, apr_pool_t *pool) 156{ 157 report_baton_t *rb = report_baton; 158 159 if (!svn_uri__is_ancestor(rb->ancestor, url)) 160 { 161 const char *ancestor; 162 163 ancestor = svn_uri_get_longest_ancestor(url, rb->ancestor, pool); 164 165 /* If we got a shorter ancestor, truncate our current ancestor. 166 Note that svn_uri_get_longest_ancestor will allocate its return 167 value even if it identical to one of its arguments. */ 168 169 rb->ancestor[strlen(ancestor)] = '\0'; 170 rb->depth = svn_depth_infinity; 171 } 172 173 return rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url, 174 revision, depth, start_empty, 175 lock_token, pool); 176} 177 178/* Implements svn_ra_reporter3_t->finish_report. */ 179static svn_error_t * 180reporter_finish_report(void *report_baton, apr_pool_t *pool) 181{ 182 report_baton_t *rb = report_baton; 183 svn_ra_session_t *ras; 184 apr_hash_t *locks; 185 const char *repos_root; 186 apr_pool_t *subpool = svn_pool_create(pool); 187 svn_error_t *err = SVN_NO_ERROR; 188 189 /* Open an RA session to our common ancestor and grab the locks under it. 190 */ 191 SVN_ERR(svn_client_open_ra_session2(&ras, rb->ancestor, NULL, 192 rb->ctx, subpool, subpool)); 193 194 /* The locks need to live throughout the edit. Note that if the 195 server doesn't support lock discovery, we'll just not do locky 196 stuff. */ 197 err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool); 198 if (err && ((err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED) 199 || (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))) 200 { 201 svn_error_clear(err); 202 err = SVN_NO_ERROR; 203 locks = apr_hash_make(rb->pool); 204 } 205 SVN_ERR(err); 206 207 SVN_ERR(svn_ra_get_repos_root2(ras, &repos_root, rb->pool)); 208 209 /* Close the RA session. */ 210 svn_pool_destroy(subpool); 211 212 SVN_ERR(svn_wc_status_set_repos_locks(rb->set_locks_baton, locks, 213 repos_root, rb->pool)); 214 215 return rb->wrapped_reporter->finish_report(rb->wrapped_report_baton, pool); 216} 217 218/* Implements svn_ra_reporter3_t->abort_report. */ 219static svn_error_t * 220reporter_abort_report(void *report_baton, apr_pool_t *pool) 221{ 222 report_baton_t *rb = report_baton; 223 224 return rb->wrapped_reporter->abort_report(rb->wrapped_report_baton, pool); 225} 226 227/* A reporter that keeps track of the common URL ancestor of all paths in 228 the WC and fetches repository locks for all paths under this ancestor. */ 229static svn_ra_reporter3_t lock_fetch_reporter = { 230 reporter_set_path, 231 reporter_delete_path, 232 reporter_link_path, 233 reporter_finish_report, 234 reporter_abort_report 235}; 236 237/* Perform status operations on each external in EXTERNAL_MAP, a const char * 238 local_abspath of all externals mapping to the const char* defining_abspath. 239 All other options are the same as those passed to svn_client_status(). 240 241 If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide 242 properly formatted relative paths */ 243static svn_error_t * 244do_external_status(svn_client_ctx_t *ctx, 245 apr_hash_t *external_map, 246 svn_depth_t depth, 247 svn_boolean_t get_all, 248 svn_boolean_t update, 249 svn_boolean_t no_ignore, 250 const char *anchor_abspath, 251 const char *anchor_relpath, 252 svn_client_status_func_t status_func, 253 void *status_baton, 254 apr_pool_t *scratch_pool) 255{ 256 apr_hash_index_t *hi; 257 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 258 259 /* Loop over the hash of new values (we don't care about the old 260 ones). This is a mapping of versioned directories to property 261 values. */ 262 for (hi = apr_hash_first(scratch_pool, external_map); 263 hi; 264 hi = apr_hash_next(hi)) 265 { 266 svn_node_kind_t external_kind; 267 const char *local_abspath = svn__apr_hash_index_key(hi); 268 const char *defining_abspath = svn__apr_hash_index_val(hi); 269 svn_node_kind_t kind; 270 svn_opt_revision_t opt_rev; 271 const char *status_path; 272 273 svn_pool_clear(iterpool); 274 275 /* Obtain information on the expected external. */ 276 SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, 277 &opt_rev.value.number, 278 ctx->wc_ctx, defining_abspath, 279 local_abspath, FALSE, 280 iterpool, iterpool)); 281 282 if (external_kind != svn_node_dir) 283 continue; 284 285 SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool)); 286 if (kind != svn_node_dir) 287 continue; 288 289 if (SVN_IS_VALID_REVNUM(opt_rev.value.number)) 290 opt_rev.kind = svn_opt_revision_number; 291 else 292 opt_rev.kind = svn_opt_revision_unspecified; 293 294 /* Tell the client we're starting an external status set. */ 295 if (ctx->notify_func2) 296 ctx->notify_func2( 297 ctx->notify_baton2, 298 svn_wc_create_notify(local_abspath, 299 svn_wc_notify_status_external, 300 iterpool), iterpool); 301 302 status_path = local_abspath; 303 if (anchor_abspath) 304 { 305 status_path = svn_dirent_join(anchor_relpath, 306 svn_dirent_skip_ancestor(anchor_abspath, 307 status_path), 308 iterpool); 309 } 310 311 /* And then do the status. */ 312 SVN_ERR(svn_client_status5(NULL, ctx, status_path, &opt_rev, depth, 313 get_all, update, no_ignore, FALSE, FALSE, 314 NULL, status_func, status_baton, 315 iterpool)); 316 } 317 318 /* Destroy SUBPOOL and (implicitly) ITERPOOL. */ 319 svn_pool_destroy(iterpool); 320 321 return SVN_NO_ERROR; 322} 323 324/*** Public Interface. ***/ 325 326 327svn_error_t * 328svn_client_status5(svn_revnum_t *result_rev, 329 svn_client_ctx_t *ctx, 330 const char *path, 331 const svn_opt_revision_t *revision, 332 svn_depth_t depth, 333 svn_boolean_t get_all, 334 svn_boolean_t update, 335 svn_boolean_t no_ignore, 336 svn_boolean_t ignore_externals, 337 svn_boolean_t depth_as_sticky, 338 const apr_array_header_t *changelists, 339 svn_client_status_func_t status_func, 340 void *status_baton, 341 apr_pool_t *pool) /* ### aka scratch_pool */ 342{ 343 struct status_baton sb; 344 const char *dir, *dir_abspath; 345 const char *target_abspath; 346 const char *target_basename; 347 apr_array_header_t *ignores; 348 svn_error_t *err; 349 apr_hash_t *changelist_hash = NULL; 350 351 if (svn_path_is_url(path)) 352 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 353 _("'%s' is not a local path"), path); 354 355 if (changelists && changelists->nelts) 356 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool)); 357 358 if (result_rev) 359 *result_rev = SVN_INVALID_REVNUM; 360 361 sb.real_status_func = status_func; 362 sb.real_status_baton = status_baton; 363 sb.deleted_in_repos = FALSE; 364 sb.changelist_hash = changelist_hash; 365 sb.wc_ctx = ctx->wc_ctx; 366 367 SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool)); 368 369 if (update) 370 { 371 /* The status editor only works on directories, so get the ancestor 372 if necessary */ 373 374 svn_node_kind_t kind; 375 376 SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath, 377 TRUE, FALSE, pool)); 378 379 /* Dir must be a working copy directory or the status editor fails */ 380 if (kind == svn_node_dir) 381 { 382 dir_abspath = target_abspath; 383 target_basename = ""; 384 dir = path; 385 } 386 else 387 { 388 dir_abspath = svn_dirent_dirname(target_abspath, pool); 389 target_basename = svn_dirent_basename(target_abspath, NULL); 390 dir = svn_dirent_dirname(path, pool); 391 392 if (kind == svn_node_file) 393 { 394 if (depth == svn_depth_empty) 395 depth = svn_depth_files; 396 } 397 else 398 { 399 err = svn_wc_read_kind2(&kind, ctx->wc_ctx, dir_abspath, 400 FALSE, FALSE, pool); 401 402 svn_error_clear(err); 403 404 if (err || kind != svn_node_dir) 405 { 406 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 407 _("'%s' is not a working copy"), 408 svn_dirent_local_style(path, pool)); 409 } 410 } 411 } 412 } 413 else 414 { 415 dir = path; 416 dir_abspath = target_abspath; 417 } 418 419 if (svn_dirent_is_absolute(dir)) 420 { 421 sb.anchor_abspath = NULL; 422 sb.anchor_relpath = NULL; 423 } 424 else 425 { 426 sb.anchor_abspath = dir_abspath; 427 sb.anchor_relpath = dir; 428 } 429 430 /* Get the status edit, and use our wrapping status function/baton 431 as the callback pair. */ 432 SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool)); 433 434 /* If we want to know about out-of-dateness, we crawl the working copy and 435 let the RA layer drive the editor for real. Otherwise, we just close the 436 edit. :-) */ 437 if (update) 438 { 439 svn_ra_session_t *ra_session; 440 const char *URL; 441 svn_node_kind_t kind; 442 svn_boolean_t server_supports_depth; 443 const svn_delta_editor_t *editor; 444 void *edit_baton, *set_locks_baton; 445 svn_revnum_t edit_revision = SVN_INVALID_REVNUM; 446 447 /* Get full URL from the ANCHOR. */ 448 SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx, 449 pool, pool)); 450 451 if (!URL) 452 return svn_error_createf 453 (SVN_ERR_ENTRY_MISSING_URL, NULL, 454 _("Entry '%s' has no URL"), 455 svn_dirent_local_style(dir, pool)); 456 457 /* Open a repository session to the URL. */ 458 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL, 459 dir_abspath, NULL, 460 FALSE, TRUE, 461 ctx, pool, pool)); 462 463 SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, 464 SVN_RA_CAPABILITY_DEPTH, pool)); 465 466 SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton, 467 &edit_revision, ctx->wc_ctx, 468 dir_abspath, target_basename, 469 depth, get_all, 470 no_ignore, depth_as_sticky, 471 server_supports_depth, 472 ignores, tweak_status, &sb, 473 ctx->cancel_func, ctx->cancel_baton, 474 pool, pool)); 475 476 477 /* Verify that URL exists in HEAD. If it doesn't, this can save 478 us a whole lot of hassle; if it does, the cost of this 479 request should be minimal compared to the size of getting 480 back the average amount of "out-of-date" information. */ 481 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, 482 &kind, pool)); 483 if (kind == svn_node_none) 484 { 485 svn_boolean_t added; 486 487 /* Our status target does not exist in HEAD. If we've got 488 it locally added, that's okay. But if it was previously 489 versioned, then it must have since been deleted from the 490 repository. (Note that "locally replaced" doesn't count 491 as "added" in this case.) */ 492 SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx, 493 dir_abspath, pool)); 494 if (! added) 495 sb.deleted_in_repos = TRUE; 496 497 /* And now close the edit. */ 498 SVN_ERR(editor->close_edit(edit_baton, pool)); 499 } 500 else 501 { 502 svn_revnum_t revnum; 503 report_baton_t rb; 504 svn_depth_t status_depth; 505 506 if (revision->kind == svn_opt_revision_head) 507 { 508 /* Cause the revision number to be omitted from the request, 509 which implies HEAD. */ 510 revnum = SVN_INVALID_REVNUM; 511 } 512 else 513 { 514 /* Get a revision number for our status operation. */ 515 SVN_ERR(svn_client__get_revision_number(&revnum, NULL, 516 ctx->wc_ctx, 517 target_abspath, 518 ra_session, revision, 519 pool)); 520 } 521 522 if (depth_as_sticky || !server_supports_depth) 523 status_depth = depth; 524 else 525 status_depth = svn_depth_unknown; /* Use depth from WC */ 526 527 /* Do the deed. Let the RA layer drive the status editor. */ 528 SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter, 529 &rb.wrapped_report_baton, 530 target_basename, revnum, status_depth, 531 editor, edit_baton, pool)); 532 533 /* Init the report baton. */ 534 rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */ 535 rb.set_locks_baton = set_locks_baton; 536 rb.ctx = ctx; 537 rb.pool = pool; 538 539 if (depth == svn_depth_unknown) 540 rb.depth = svn_depth_infinity; 541 else 542 rb.depth = depth; 543 544 /* Drive the reporter structure, describing the revisions 545 within PATH. When we call reporter->finish_report, 546 EDITOR will be driven to describe differences between our 547 working copy and HEAD. */ 548 SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, 549 target_abspath, 550 &lock_fetch_reporter, &rb, 551 FALSE /* restore_files */, 552 depth, (! depth_as_sticky), 553 (! server_supports_depth), 554 FALSE /* use_commit_times */, 555 ctx->cancel_func, ctx->cancel_baton, 556 NULL, NULL, pool)); 557 } 558 559 if (ctx->notify_func2) 560 { 561 svn_wc_notify_t *notify 562 = svn_wc_create_notify(target_abspath, 563 svn_wc_notify_status_completed, pool); 564 notify->revision = edit_revision; 565 (ctx->notify_func2)(ctx->notify_baton2, notify, pool); 566 } 567 568 /* If the caller wants the result revision, give it to them. */ 569 if (result_rev) 570 *result_rev = edit_revision; 571 } 572 else 573 { 574 err = svn_wc_walk_status(ctx->wc_ctx, target_abspath, 575 depth, get_all, no_ignore, FALSE, ignores, 576 tweak_status, &sb, 577 ctx->cancel_func, ctx->cancel_baton, 578 pool); 579 580 if (err && err->apr_err == SVN_ERR_WC_MISSING) 581 { 582 /* This error code is checked for in svn to continue after 583 this error */ 584 svn_error_clear(err); 585 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 586 _("'%s' is not a working copy"), 587 svn_dirent_local_style(path, pool)); 588 } 589 590 SVN_ERR(err); 591 } 592 593 /* If there are svn:externals set, we don't want those to show up as 594 unversioned or unrecognized, so patch up the hash. If caller wants 595 all the statuses, we will change unversioned status items that 596 are interesting to an svn:externals property to 597 svn_wc_status_unversioned, otherwise we'll just remove the status 598 item altogether. 599 600 We only descend into an external if depth is svn_depth_infinity or 601 svn_depth_unknown. However, there are conceivable behaviors that 602 would involve descending under other circumstances; thus, we pass 603 depth anyway, so the code will DTRT if we change the conditional 604 in the future. 605 */ 606 if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) 607 { 608 apr_hash_t *external_map; 609 SVN_ERR(svn_wc__externals_defined_below(&external_map, 610 ctx->wc_ctx, target_abspath, 611 pool, pool)); 612 613 614 SVN_ERR(do_external_status(ctx, external_map, 615 depth, get_all, 616 update, no_ignore, 617 sb.anchor_abspath, sb.anchor_relpath, 618 status_func, status_baton, pool)); 619 } 620 621 return SVN_NO_ERROR; 622} 623 624svn_client_status_t * 625svn_client_status_dup(const svn_client_status_t *status, 626 apr_pool_t *result_pool) 627{ 628 svn_client_status_t *st = apr_palloc(result_pool, sizeof(*st)); 629 630 *st = *status; 631 632 if (status->local_abspath) 633 st->local_abspath = apr_pstrdup(result_pool, status->local_abspath); 634 635 if (status->repos_root_url) 636 st->repos_root_url = apr_pstrdup(result_pool, status->repos_root_url); 637 638 if (status->repos_uuid) 639 st->repos_uuid = apr_pstrdup(result_pool, status->repos_uuid); 640 641 if (status->repos_relpath) 642 st->repos_relpath = apr_pstrdup(result_pool, status->repos_relpath); 643 644 if (status->changed_author) 645 st->changed_author = apr_pstrdup(result_pool, status->changed_author); 646 647 if (status->lock) 648 st->lock = svn_lock_dup(status->lock, result_pool); 649 650 if (status->changelist) 651 st->changelist = apr_pstrdup(result_pool, status->changelist); 652 653 if (status->ood_changed_author) 654 st->ood_changed_author = apr_pstrdup(result_pool, status->ood_changed_author); 655 656 if (status->repos_lock) 657 st->repos_lock = svn_lock_dup(status->repos_lock, result_pool); 658 659 if (status->backwards_compatibility_baton) 660 { 661 const svn_wc_status3_t *wc_st = status->backwards_compatibility_baton; 662 663 st->backwards_compatibility_baton = svn_wc_dup_status3(wc_st, 664 result_pool); 665 } 666 667 if (status->moved_from_abspath) 668 st->moved_from_abspath = 669 apr_pstrdup(result_pool, status->moved_from_abspath); 670 671 if (status->moved_to_abspath) 672 st->moved_to_abspath = apr_pstrdup(result_pool, status->moved_to_abspath); 673 674 return st; 675} 676 677svn_error_t * 678svn_client__create_status(svn_client_status_t **cst, 679 svn_wc_context_t *wc_ctx, 680 const char *local_abspath, 681 const svn_wc_status3_t *status, 682 apr_pool_t *result_pool, 683 apr_pool_t *scratch_pool) 684{ 685 *cst = apr_pcalloc(result_pool, sizeof(**cst)); 686 687 (*cst)->kind = status->kind; 688 (*cst)->local_abspath = local_abspath; 689 (*cst)->filesize = status->filesize; 690 (*cst)->versioned = status->versioned; 691 692 (*cst)->conflicted = status->conflicted; 693 694 (*cst)->node_status = status->node_status; 695 (*cst)->text_status = status->text_status; 696 (*cst)->prop_status = status->prop_status; 697 698 if (status->kind == svn_node_dir) 699 (*cst)->wc_is_locked = status->locked; 700 701 (*cst)->copied = status->copied; 702 (*cst)->revision = status->revision; 703 704 (*cst)->changed_rev = status->changed_rev; 705 (*cst)->changed_date = status->changed_date; 706 (*cst)->changed_author = status->changed_author; 707 708 (*cst)->repos_root_url = status->repos_root_url; 709 (*cst)->repos_uuid = status->repos_uuid; 710 (*cst)->repos_relpath = status->repos_relpath; 711 712 (*cst)->switched = status->switched; 713 714 (*cst)->file_external = status->file_external; 715 if (status->file_external) 716 { 717 (*cst)->switched = FALSE; 718 } 719 720 (*cst)->lock = status->lock; 721 722 (*cst)->changelist = status->changelist; 723 (*cst)->depth = status->depth; 724 725 /* Out of date information */ 726 (*cst)->ood_kind = status->ood_kind; 727 (*cst)->repos_node_status = status->repos_node_status; 728 (*cst)->repos_text_status = status->repos_text_status; 729 (*cst)->repos_prop_status = status->repos_prop_status; 730 (*cst)->repos_lock = status->repos_lock; 731 732 (*cst)->ood_changed_rev = status->ood_changed_rev; 733 (*cst)->ood_changed_date = status->ood_changed_date; 734 (*cst)->ood_changed_author = status->ood_changed_author; 735 736 /* When changing the value of backwards_compatibility_baton, also 737 change its use in status4_wrapper_func in deprecated.c */ 738 (*cst)->backwards_compatibility_baton = status; 739 740 if (status->versioned && status->conflicted) 741 { 742 svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted; 743 744 /* Note: This checks the on disk markers to automatically hide 745 text/property conflicts that are hidden by removing their 746 markers */ 747 SVN_ERR(svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted, 748 &tree_conflicted, wc_ctx, local_abspath, 749 scratch_pool)); 750 751 if (text_conflicted) 752 (*cst)->text_status = svn_wc_status_conflicted; 753 754 if (prop_conflicted) 755 (*cst)->prop_status = svn_wc_status_conflicted; 756 757 /* ### Also set this for tree_conflicts? */ 758 if (text_conflicted || prop_conflicted) 759 (*cst)->node_status = svn_wc_status_conflicted; 760 } 761 762 (*cst)->moved_from_abspath = status->moved_from_abspath; 763 (*cst)->moved_to_abspath = status->moved_to_abspath; 764 765 return SVN_NO_ERROR; 766} 767 768