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