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