entries.c revision 299742
1/* 2 * entries.c : manipulating the administrative `entries' file. 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#include <string.h> 25#include <assert.h> 26 27#include <apr_strings.h> 28 29#include "svn_error.h" 30#include "svn_types.h" 31#include "svn_time.h" 32#include "svn_pools.h" 33#include "svn_dirent_uri.h" 34#include "svn_path.h" 35#include "svn_ctype.h" 36#include "svn_string.h" 37#include "svn_hash.h" 38 39#include "wc.h" 40#include "adm_files.h" 41#include "conflicts.h" 42#include "entries.h" 43#include "lock.h" 44#include "tree_conflicts.h" 45#include "wc_db.h" 46#include "wc-queries.h" /* for STMT_* */ 47 48#define SVN_WC__I_AM_WC_DB 49 50#include "svn_private_config.h" 51#include "private/svn_wc_private.h" 52#include "private/svn_sqlite.h" 53#include "token-map.h" 54 55#include "wc_db_private.h" 56 57#define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x)))) 58 59 60/* Temporary structures which mirror the tables in wc-metadata.sql. 61 For detailed descriptions of each field, see that file. */ 62typedef struct db_node_t { 63 apr_int64_t wc_id; 64 const char *local_relpath; 65 int op_depth; 66 apr_int64_t repos_id; 67 const char *repos_relpath; 68 const char *parent_relpath; 69 svn_wc__db_status_t presence; 70 svn_revnum_t revision; 71 svn_node_kind_t kind; 72 svn_checksum_t *checksum; 73 svn_filesize_t recorded_size; 74 svn_revnum_t changed_rev; 75 apr_time_t changed_date; 76 const char *changed_author; 77 svn_depth_t depth; 78 apr_time_t recorded_time; 79 apr_hash_t *properties; 80 svn_boolean_t file_external; 81 apr_array_header_t *inherited_props; 82} db_node_t; 83 84typedef struct db_actual_node_t { 85 apr_int64_t wc_id; 86 const char *local_relpath; 87 const char *parent_relpath; 88 apr_hash_t *properties; 89 const char *conflict_old; 90 const char *conflict_new; 91 const char *conflict_working; 92 const char *prop_reject; 93 const char *changelist; 94 /* ### enum for text_mod */ 95 const char *tree_conflict_data; 96} db_actual_node_t; 97 98 99 100/*** reading and writing the entries file ***/ 101 102 103/* */ 104static svn_wc_entry_t * 105alloc_entry(apr_pool_t *pool) 106{ 107 svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry)); 108 entry->revision = SVN_INVALID_REVNUM; 109 entry->copyfrom_rev = SVN_INVALID_REVNUM; 110 entry->cmt_rev = SVN_INVALID_REVNUM; 111 entry->kind = svn_node_none; 112 entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN; 113 entry->depth = svn_depth_infinity; 114 entry->file_external_peg_rev.kind = svn_opt_revision_unspecified; 115 entry->file_external_rev.kind = svn_opt_revision_unspecified; 116 return entry; 117} 118 119 120/* Is the entry in a 'hidden' state in the sense of the 'show_hidden' 121 * switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */ 122svn_error_t * 123svn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry) 124{ 125 /* In English, the condition is: "the entry is not present, and I haven't 126 scheduled something over the top of it." */ 127 if (entry->deleted 128 || entry->absent 129 || entry->depth == svn_depth_exclude) 130 { 131 /* These kinds of nodes cannot be marked for deletion (which also 132 means no "replace" either). */ 133 SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add 134 || entry->schedule == svn_wc_schedule_normal); 135 136 /* Hidden if something hasn't been added over it. 137 138 ### is this even possible with absent or excluded nodes? */ 139 *hidden = entry->schedule != svn_wc_schedule_add; 140 } 141 else 142 *hidden = FALSE; 143 144 return SVN_NO_ERROR; 145} 146 147 148/* Hit the database to check the file external information for the given 149 entry. The entry will be modified in place. */ 150static svn_error_t * 151check_file_external(svn_wc_entry_t *entry, 152 svn_wc__db_t *db, 153 const char *local_abspath, 154 const char *wri_abspath, 155 apr_pool_t *result_pool, 156 apr_pool_t *scratch_pool) 157{ 158 svn_wc__db_status_t status; 159 svn_node_kind_t kind; 160 const char *repos_relpath; 161 svn_revnum_t peg_revision; 162 svn_revnum_t revision; 163 svn_error_t *err; 164 165 err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL, 166 &repos_relpath, &peg_revision, &revision, 167 db, local_abspath, wri_abspath, 168 result_pool, scratch_pool); 169 170 if (err) 171 { 172 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 173 return svn_error_trace(err); 174 175 svn_error_clear(err); 176 return SVN_NO_ERROR; 177 } 178 179 if (status == svn_wc__db_status_normal 180 && kind == svn_node_file) 181 { 182 entry->file_external_path = repos_relpath; 183 if (SVN_IS_VALID_REVNUM(peg_revision)) 184 { 185 entry->file_external_peg_rev.kind = svn_opt_revision_number; 186 entry->file_external_peg_rev.value.number = peg_revision; 187 entry->file_external_rev = entry->file_external_peg_rev; 188 } 189 if (SVN_IS_VALID_REVNUM(revision)) 190 { 191 entry->file_external_rev.kind = svn_opt_revision_number; 192 entry->file_external_rev.value.number = revision; 193 } 194 } 195 196 return SVN_NO_ERROR; 197} 198 199 200/* Fill in the following fields of ENTRY: 201 202 REVISION 203 REPOS 204 UUID 205 CMT_REV 206 CMT_DATE 207 CMT_AUTHOR 208 DEPTH 209 DELETED 210 211 Return: KIND, REPOS_RELPATH, CHECKSUM 212*/ 213static svn_error_t * 214get_info_for_deleted(svn_wc_entry_t *entry, 215 svn_node_kind_t *kind, 216 const char **repos_relpath, 217 const svn_checksum_t **checksum, 218 svn_wc__db_lock_t **lock, 219 svn_wc__db_t *db, 220 const char *entry_abspath, 221 svn_wc__db_wcroot_t *wcroot, 222 const char *entry_relpath, 223 const svn_wc_entry_t *parent_entry, 224 svn_boolean_t have_base, 225 svn_boolean_t have_more_work, 226 apr_pool_t *result_pool, 227 apr_pool_t *scratch_pool) 228{ 229 if (have_base && !have_more_work) 230 { 231 apr_int64_t repos_id; 232 /* This is the delete of a BASE node */ 233 SVN_ERR(svn_wc__db_base_get_info_internal( 234 NULL, kind, 235 &entry->revision, 236 repos_relpath, 237 &repos_id, 238 &entry->cmt_rev, 239 &entry->cmt_date, 240 &entry->cmt_author, 241 &entry->depth, 242 checksum, 243 NULL, 244 lock, 245 &entry->has_props, NULL, 246 NULL, 247 wcroot, entry_relpath, 248 result_pool, 249 scratch_pool)); 250 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, 251 wcroot, repos_id, result_pool)); 252 } 253 else 254 { 255 const char *work_del_relpath; 256 const char *parent_repos_relpath; 257 const char *parent_relpath; 258 apr_int64_t repos_id; 259 260 /* This is a deleted child of a copy/move-here, 261 so we need to scan up the WORKING tree to find the root of 262 the deletion. Then examine its parent to discover its 263 future location in the repository. */ 264 SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind, 265 &entry->cmt_rev, 266 &entry->cmt_date, 267 &entry->cmt_author, 268 &entry->depth, 269 checksum, 270 NULL, 271 &entry->has_props, NULL, 272 db, 273 entry_abspath, 274 result_pool, 275 scratch_pool)); 276 /* working_size and text_time unavailable */ 277 278 SVN_ERR(svn_wc__db_scan_deletion_internal( 279 NULL, 280 NULL, 281 &work_del_relpath, NULL, 282 wcroot, entry_relpath, 283 scratch_pool, scratch_pool)); 284 285 SVN_ERR_ASSERT(work_del_relpath != NULL); 286 parent_relpath = svn_relpath_dirname(work_del_relpath, scratch_pool); 287 288 /* The parent directory of the delete root must be added, so we 289 can find the required information there */ 290 SVN_ERR(svn_wc__db_scan_addition_internal( 291 NULL, NULL, 292 &parent_repos_relpath, 293 &repos_id, 294 NULL, NULL, NULL, 295 wcroot, parent_relpath, 296 result_pool, scratch_pool)); 297 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, 298 wcroot, repos_id, result_pool)); 299 300 /* Now glue it all together */ 301 *repos_relpath = svn_relpath_join(parent_repos_relpath, 302 svn_relpath_skip_ancestor( 303 parent_relpath, 304 entry_relpath), 305 result_pool); 306 307 308 /* Even though this is the delete of a WORKING node, there might still 309 be a BASE node somewhere below with an interesting revision */ 310 if (have_base) 311 { 312 svn_wc__db_status_t status; 313 SVN_ERR(svn_wc__db_base_get_info_internal( 314 &status, NULL, &entry->revision, 315 NULL, NULL, NULL, NULL, NULL, NULL, 316 NULL, NULL, lock, NULL, NULL, 317 NULL, 318 wcroot, entry_relpath, 319 result_pool, scratch_pool)); 320 321 if (status == svn_wc__db_status_not_present) 322 entry->deleted = TRUE; 323 } 324 } 325 326 /* Do some extra work for the child nodes. */ 327 if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL) 328 { 329 /* For child nodes without a revision, pick up the parent's 330 revision. */ 331 entry->revision = parent_entry->revision; 332 } 333 334 return SVN_NO_ERROR; 335} 336 337 338/* 339 * Encode tree conflict descriptions into a single string. 340 * 341 * Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree 342 * conflicts in CONFLICTS in a form suitable for storage in a single string 343 * field in a WC entry. CONFLICTS is a hash of zero or more pointers to 344 * svn_wc_conflict_description2_t objects, index by their basenames. All of the 345 * conflict victim paths must be siblings. 346 * 347 * Do all allocations in POOL. 348 * 349 * @see svn_wc__read_tree_conflicts() 350 */ 351static svn_error_t * 352write_tree_conflicts(const char **conflict_data, 353 apr_hash_t *conflicts, 354 apr_pool_t *pool) 355{ 356 svn_skel_t *skel = svn_skel__make_empty_list(pool); 357 apr_hash_index_t *hi; 358 359 for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi)) 360 { 361 svn_skel_t *c_skel; 362 363 SVN_ERR(svn_wc__serialize_conflict(&c_skel, apr_hash_this_val(hi), 364 pool, pool)); 365 svn_skel__prepend(c_skel, skel); 366 } 367 368 *conflict_data = svn_skel__unparse(skel, pool)->data; 369 370 return SVN_NO_ERROR; 371} 372 373 374/* Read one entry from wc_db. It will be allocated in RESULT_POOL and 375 returned in *NEW_ENTRY. 376 377 DIR_ABSPATH is the name of the directory to read this entry from, and 378 it will be named NAME (use "" for "this dir"). 379 380 DB specifies the wc_db database, and WC_ID specifies which working copy 381 this information is being read from. 382 383 If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise, 384 it should refer to the entry for the child's parent directory. 385 386 ### All database read operations should really use wcroot, dir_relpath, 387 as that restores obstruction compatibility with <= 1.6.0 388 but that has been the case since the introduction of WC-NG in 1.7.0 389 390 Temporary allocations are made in SCRATCH_POOL. */ 391static svn_error_t * 392read_one_entry(const svn_wc_entry_t **new_entry, 393 svn_wc__db_t *db, 394 const char *dir_abspath, 395 svn_wc__db_wcroot_t *wcroot, 396 const char *dir_relpath, 397 const char *name, 398 const svn_wc_entry_t *parent_entry, 399 apr_pool_t *result_pool, 400 apr_pool_t *scratch_pool) 401{ 402 svn_node_kind_t kind; 403 svn_wc__db_status_t status; 404 svn_wc__db_lock_t *lock; 405 const char *repos_relpath; 406 const svn_checksum_t *checksum; 407 svn_filesize_t translated_size; 408 svn_wc_entry_t *entry = alloc_entry(result_pool); 409 const char *entry_relpath; 410 const char *entry_abspath; 411 apr_int64_t repos_id; 412 apr_int64_t original_repos_id; 413 const char *original_repos_relpath; 414 const char *original_root_url; 415 svn_boolean_t conflicted; 416 svn_boolean_t have_base; 417 svn_boolean_t have_more_work; 418 svn_boolean_t op_root; 419 420 entry->name = apr_pstrdup(result_pool, name); 421 422 entry_relpath = svn_relpath_join(dir_relpath, entry->name, scratch_pool); 423 entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool); 424 425 SVN_ERR(svn_wc__db_read_info_internal( 426 &status, 427 &kind, 428 &entry->revision, 429 &repos_relpath, 430 &repos_id, 431 &entry->cmt_rev, 432 &entry->cmt_date, 433 &entry->cmt_author, 434 &entry->depth, 435 &checksum, 436 NULL, 437 &original_repos_relpath, 438 &original_repos_id, 439 &entry->copyfrom_rev, 440 &lock, 441 &translated_size, 442 &entry->text_time, 443 &entry->changelist, 444 &conflicted, 445 &op_root, 446 &entry->has_props /* have_props */, 447 &entry->has_prop_mods /* props_mod */, 448 &have_base, 449 &have_more_work, 450 NULL /* have_work */, 451 wcroot, entry_relpath, 452 result_pool, scratch_pool)); 453 454 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, 455 wcroot, repos_id, result_pool)); 456 SVN_ERR(svn_wc__db_fetch_repos_info(&original_root_url, NULL, 457 wcroot, original_repos_id, 458 result_pool)); 459 460 if (entry->has_prop_mods) 461 entry->has_props = TRUE; 462 463 if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0) 464 { 465 /* get the tree conflict data. */ 466 apr_hash_t *tree_conflicts = NULL; 467 const apr_array_header_t *conflict_victims; 468 int k; 469 470 SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db, 471 dir_abspath, 472 scratch_pool, 473 scratch_pool)); 474 475 for (k = 0; k < conflict_victims->nelts; k++) 476 { 477 int j; 478 const apr_array_header_t *child_conflicts; 479 const char *child_name; 480 const char *child_abspath; 481 482 child_name = APR_ARRAY_IDX(conflict_victims, k, const char *); 483 child_abspath = svn_dirent_join(dir_abspath, child_name, 484 scratch_pool); 485 486 SVN_ERR(svn_wc__read_conflicts(&child_conflicts, NULL, 487 db, child_abspath, 488 FALSE /* create tempfiles */, 489 TRUE /* tree_conflicts_only */, 490 scratch_pool, scratch_pool)); 491 492 for (j = 0; j < child_conflicts->nelts; j++) 493 { 494 const svn_wc_conflict_description2_t *conflict = 495 APR_ARRAY_IDX(child_conflicts, j, 496 svn_wc_conflict_description2_t *); 497 498 if (conflict->kind == svn_wc_conflict_kind_tree) 499 { 500 if (!tree_conflicts) 501 tree_conflicts = apr_hash_make(scratch_pool); 502 svn_hash_sets(tree_conflicts, child_name, conflict); 503 } 504 } 505 } 506 507 if (tree_conflicts) 508 { 509 SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data, 510 tree_conflicts, result_pool)); 511 } 512 } 513 514 if (status == svn_wc__db_status_normal 515 || status == svn_wc__db_status_incomplete) 516 { 517 /* Plain old BASE node. */ 518 entry->schedule = svn_wc_schedule_normal; 519 520 /* Grab inherited repository information, if necessary. */ 521 if (repos_relpath == NULL) 522 { 523 SVN_ERR(svn_wc__db_base_get_info_internal( 524 NULL, NULL, NULL, &repos_relpath, 525 &repos_id, NULL, NULL, NULL, 526 NULL, NULL, NULL, NULL, NULL, NULL, 527 NULL, 528 wcroot, entry_relpath, 529 result_pool, scratch_pool)); 530 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, 531 wcroot, repos_id, result_pool)); 532 } 533 534 entry->incomplete = (status == svn_wc__db_status_incomplete); 535 } 536 else if (status == svn_wc__db_status_deleted) 537 { 538 svn_node_kind_t path_kind; 539 540 /* ### we don't have to worry about moves, so this is a delete. */ 541 entry->schedule = svn_wc_schedule_delete; 542 543 /* If there are multiple working layers or no BASE layer, then 544 this is a WORKING delete or WORKING not-present. */ 545 if (have_more_work || !have_base) 546 entry->copied = TRUE; 547 else if (have_base && !have_more_work) 548 entry->copied = FALSE; 549 else 550 { 551 const char *work_del_relpath; 552 SVN_ERR(svn_wc__db_scan_deletion_internal( 553 NULL, NULL, 554 &work_del_relpath, NULL, 555 wcroot, entry_relpath, 556 scratch_pool, scratch_pool)); 557 558 if (work_del_relpath) 559 entry->copied = TRUE; 560 } 561 562 /* If there is still a directory on-disk we keep it, if not it is 563 already deleted. Simple, isn't it? 564 565 Before single-db we had to keep the administative area alive until 566 after the commit really deletes it. Setting keep alive stopped the 567 commit processing from deleting the directory. We don't delete it 568 any more, so all we have to do is provide some 'sane' value. 569 */ 570 SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool)); 571 entry->keep_local = (path_kind == svn_node_dir); 572 } 573 else if (status == svn_wc__db_status_added) 574 { 575 svn_wc__db_status_t work_status; 576 const char *op_root_abspath; 577 const char *scanned_original_relpath; 578 svn_revnum_t original_revision; 579 580 /* For child nodes, pick up the parent's revision. */ 581 if (*entry->name != '\0') 582 { 583 assert(parent_entry != NULL); 584 assert(entry->revision == SVN_INVALID_REVNUM); 585 586 entry->revision = parent_entry->revision; 587 } 588 589 if (have_base) 590 { 591 svn_wc__db_status_t base_status; 592 593 /* ENTRY->REVISION is overloaded. When a node is schedule-add 594 or -replace, then REVISION refers to the BASE node's revision 595 that is being overwritten. We need to fetch it now. */ 596 SVN_ERR(svn_wc__db_base_get_info_internal( 597 &base_status, NULL, 598 &entry->revision, 599 NULL, NULL, NULL, 600 NULL, NULL, NULL, 601 NULL, NULL, NULL, 602 NULL, NULL, NULL, 603 wcroot, entry_relpath, 604 scratch_pool, 605 scratch_pool)); 606 607 if (base_status == svn_wc__db_status_not_present) 608 { 609 /* The underlying node is DELETED in this revision. */ 610 entry->deleted = TRUE; 611 612 /* This is an add since there isn't a node to replace. */ 613 entry->schedule = svn_wc_schedule_add; 614 } 615 else 616 entry->schedule = svn_wc_schedule_replace; 617 } 618 else 619 { 620 /* There is NO 'not-present' BASE_NODE for this node. 621 Therefore, we are looking at some kind of add/copy 622 rather than a replace. */ 623 624 /* ### if this looks like a plain old add, then rev=0. */ 625 if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev) 626 && !SVN_IS_VALID_REVNUM(entry->cmt_rev)) 627 entry->revision = 0; 628 629 entry->schedule = svn_wc_schedule_add; 630 } 631 632 /* If we don't have "real" data from the entry (obstruction), 633 then we cannot begin a scan for data. The original node may 634 have important data. Set up stuff to kill that idea off, 635 and finish up this entry. */ 636 { 637 const char *op_root_relpath; 638 SVN_ERR(svn_wc__db_scan_addition_internal( 639 &work_status, 640 &op_root_relpath, 641 &repos_relpath, 642 &repos_id, 643 &scanned_original_relpath, 644 NULL /* original_repos_id */, 645 &original_revision, 646 wcroot, entry_relpath, 647 result_pool, scratch_pool)); 648 649 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid, 650 wcroot, repos_id, result_pool)); 651 652 if (!op_root_relpath) 653 op_root_abspath = NULL; 654 else 655 op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath, 656 scratch_pool); 657 658 /* In wc.db we want to keep the valid revision of the not-present 659 BASE_REV, but when we used entries we set the revision to 0 660 when adding a new node over a not present base node. */ 661 if (work_status == svn_wc__db_status_added 662 && entry->deleted) 663 entry->revision = 0; 664 } 665 666 if (!SVN_IS_VALID_REVNUM(entry->cmt_rev) 667 && scanned_original_relpath == NULL) 668 { 669 /* There is NOT a last-changed revision (last-changed date and 670 author may be unknown, but we can always check the rev). 671 The absence of a revision implies this node was added WITHOUT 672 any history. Avoid the COPIED checks in the else block. */ 673 /* ### scan_addition may need to be updated to avoid returning 674 ### status_copied in this case. */ 675 } 676 /* For backwards-compatibility purposes we treat moves just like 677 * regular copies. */ 678 else if (work_status == svn_wc__db_status_copied || 679 work_status == svn_wc__db_status_moved_here) 680 { 681 entry->copied = TRUE; 682 683 /* If this is a child of a copied subtree, then it should be 684 schedule_normal. */ 685 if (original_repos_relpath == NULL) 686 { 687 /* ### what if there is a BASE node under there? */ 688 entry->schedule = svn_wc_schedule_normal; 689 } 690 691 /* Copied nodes need to mirror their copyfrom_rev, if they 692 don't have a revision of their own already. */ 693 if (!SVN_IS_VALID_REVNUM(entry->revision) 694 || entry->revision == 0 /* added */) 695 entry->revision = original_revision; 696 } 697 698 /* Does this node have copyfrom_* information? */ 699 if (scanned_original_relpath != NULL) 700 { 701 svn_boolean_t is_copied_child; 702 svn_boolean_t is_mixed_rev = FALSE; 703 704 SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied || 705 work_status == svn_wc__db_status_moved_here); 706 707 /* If this node inherits copyfrom information from an 708 ancestor node, then it must be a copied child. */ 709 is_copied_child = (original_repos_relpath == NULL); 710 711 /* If this node has copyfrom information on it, then it may 712 be an actual copy-root, or it could be participating in 713 a mixed-revision copied tree. So if we don't already know 714 this is a copied child, then we need to look for this 715 mixed-revision situation. */ 716 if (!is_copied_child) 717 { 718 const char *parent_relpath; 719 svn_error_t *err; 720 const char *parent_repos_relpath; 721 const char *parent_root_url; 722 apr_int64_t parent_repos_id; 723 const char *op_root_relpath; 724 725 /* When we insert entries into the database, we will 726 construct additional copyfrom records for mixed-revision 727 copies. The old entries would simply record the different 728 revision in the entry->revision field. That is not 729 available within wc-ng, so additional copies are made 730 (see the logic inside write_entry()). However, when 731 reading these back *out* of the database, the additional 732 copies look like new "Added" nodes rather than a simple 733 mixed-rev working copy. 734 735 That would be a behavior change if we did not compensate. 736 If there is copyfrom information for this node, then the 737 code below looks at the parent to detect if it *also* has 738 copyfrom information, and if the copyfrom_url would align 739 properly. If it *does*, then we omit storing copyfrom_url 740 and copyfrom_rev (ie. inherit the copyfrom info like a 741 normal child), and update entry->revision with the 742 copyfrom_rev in order to (re)create the mixed-rev copied 743 subtree that was originally presented for storage. */ 744 745 /* Get the copyfrom information from our parent. 746 747 Note that the parent could be added/copied/moved-here. 748 There is no way for it to be deleted/moved-away and 749 have *this* node appear as copied. */ 750 parent_relpath = svn_relpath_dirname(entry_relpath, 751 scratch_pool); 752 err = svn_wc__db_scan_addition_internal( 753 NULL, 754 &op_root_relpath, 755 NULL, NULL, 756 &parent_repos_relpath, 757 &parent_repos_id, 758 NULL, 759 wcroot, parent_relpath, 760 scratch_pool, 761 scratch_pool); 762 if (err) 763 { 764 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 765 return svn_error_trace(err); 766 svn_error_clear(err); 767 op_root_abspath = NULL; 768 parent_repos_relpath = NULL; 769 parent_root_url = NULL; 770 } 771 else 772 { 773 SVN_ERR(svn_wc__db_fetch_repos_info(&parent_root_url, NULL, 774 wcroot, parent_repos_id, 775 scratch_pool)); 776 op_root_abspath = svn_dirent_join(wcroot->abspath, 777 op_root_relpath, 778 scratch_pool); 779 } 780 781 if (parent_root_url != NULL 782 && strcmp(original_root_url, parent_root_url) == 0) 783 { 784 785 const char *relpath_to_entry = svn_dirent_is_child( 786 op_root_abspath, entry_abspath, NULL); 787 const char *entry_repos_relpath = svn_relpath_join( 788 parent_repos_relpath, relpath_to_entry, scratch_pool); 789 790 /* The copyfrom repos roots matched. 791 792 Now we look to see if the copyfrom path of the parent 793 would align with our own path. If so, then it means 794 this copyfrom was spontaneously created and inserted 795 for mixed-rev purposes and can be eliminated without 796 changing the semantics of a mixed-rev copied subtree. 797 798 See notes/api-errata/wc003.txt for some additional 799 detail, and potential issues. */ 800 if (strcmp(entry_repos_relpath, 801 original_repos_relpath) == 0) 802 { 803 is_copied_child = TRUE; 804 is_mixed_rev = TRUE; 805 } 806 } 807 } 808 809 if (is_copied_child) 810 { 811 /* We won't be settig the copyfrom_url, yet need to 812 clear out the copyfrom_rev. Thus, this node becomes a 813 child of a copied subtree (rather than its own root). */ 814 entry->copyfrom_rev = SVN_INVALID_REVNUM; 815 816 /* Children in a copied subtree are schedule normal 817 since we don't plan to actually *do* anything with 818 them. Their operation is implied by ancestors. */ 819 entry->schedule = svn_wc_schedule_normal; 820 821 /* And *finally* we turn this entry into the mixed 822 revision node that it was intended to be. This 823 node's revision is taken from the copyfrom record 824 that we spontaneously constructed. */ 825 if (is_mixed_rev) 826 entry->revision = original_revision; 827 } 828 else if (original_repos_relpath != NULL) 829 { 830 entry->copyfrom_url = 831 svn_path_url_add_component2(original_root_url, 832 original_repos_relpath, 833 result_pool); 834 } 835 else 836 { 837 /* NOTE: if original_repos_relpath == NULL, then the 838 second call to scan_addition() will not have occurred. 839 Thus, this use of OP_ROOT_ABSPATH still contains the 840 original value where we fetched a value for 841 SCANNED_REPOS_RELPATH. */ 842 const char *relpath_to_entry = svn_dirent_is_child( 843 op_root_abspath, entry_abspath, NULL); 844 const char *entry_repos_relpath = svn_relpath_join( 845 scanned_original_relpath, relpath_to_entry, scratch_pool); 846 847 entry->copyfrom_url = 848 svn_path_url_add_component2(original_root_url, 849 entry_repos_relpath, 850 result_pool); 851 } 852 } 853 } 854 else if (status == svn_wc__db_status_not_present) 855 { 856 /* ### buh. 'deleted' nodes are actually supposed to be 857 ### schedule "normal" since we aren't going to actually *do* 858 ### anything to this node at commit time. */ 859 entry->schedule = svn_wc_schedule_normal; 860 entry->deleted = TRUE; 861 } 862 else if (status == svn_wc__db_status_server_excluded) 863 { 864 entry->absent = TRUE; 865 } 866 else if (status == svn_wc__db_status_excluded) 867 { 868 entry->schedule = svn_wc_schedule_normal; 869 entry->depth = svn_depth_exclude; 870 } 871 else 872 { 873 /* ### we should have handled all possible status values. */ 874 SVN_ERR_MALFUNCTION(); 875 } 876 877 /* ### higher levels want repos information about deleted nodes, even 878 ### tho they are not "part of" a repository any more. */ 879 if (entry->schedule == svn_wc_schedule_delete) 880 { 881 SVN_ERR(get_info_for_deleted(entry, 882 &kind, 883 &repos_relpath, 884 &checksum, 885 &lock, 886 db, entry_abspath, 887 wcroot, entry_relpath, 888 parent_entry, 889 have_base, have_more_work, 890 result_pool, scratch_pool)); 891 } 892 893 /* ### default to the infinite depth if we don't know it. */ 894 if (entry->depth == svn_depth_unknown) 895 entry->depth = svn_depth_infinity; 896 897 if (kind == svn_node_dir) 898 entry->kind = svn_node_dir; 899 else if (kind == svn_node_file) 900 entry->kind = svn_node_file; 901 else if (kind == svn_node_symlink) 902 entry->kind = svn_node_file; /* ### no symlink kind */ 903 else 904 entry->kind = svn_node_unknown; 905 906 /* We should always have a REPOS_RELPATH, except for: 907 - deleted nodes 908 - certain obstructed nodes 909 - not-present nodes 910 - absent nodes 911 - excluded nodes 912 913 ### the last three should probably have an "implied" REPOS_RELPATH 914 */ 915 SVN_ERR_ASSERT(repos_relpath != NULL 916 || entry->schedule == svn_wc_schedule_delete 917 || status == svn_wc__db_status_not_present 918 || status == svn_wc__db_status_server_excluded 919 || status == svn_wc__db_status_excluded); 920 if (repos_relpath) 921 entry->url = svn_path_url_add_component2(entry->repos, 922 repos_relpath, 923 result_pool); 924 925 if (checksum) 926 { 927 /* We got a SHA-1, get the corresponding MD-5. */ 928 if (checksum->kind != svn_checksum_md5) 929 SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db, 930 dir_abspath, checksum, 931 scratch_pool, scratch_pool)); 932 933 SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5); 934 entry->checksum = svn_checksum_to_cstring(checksum, result_pool); 935 } 936 937 if (conflicted) 938 { 939 svn_skel_t *conflict; 940 svn_boolean_t text_conflicted; 941 svn_boolean_t prop_conflicted; 942 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, 943 wcroot, entry_relpath, 944 scratch_pool, scratch_pool)); 945 946 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted, 947 &prop_conflicted, NULL, 948 db, dir_abspath, conflict, 949 scratch_pool, scratch_pool)); 950 951 if (text_conflicted) 952 { 953 const char *my_abspath; 954 const char *their_old_abspath; 955 const char *their_abspath; 956 SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath, 957 &their_old_abspath, 958 &their_abspath, 959 db, dir_abspath, 960 conflict, scratch_pool, 961 scratch_pool)); 962 963 if (my_abspath) 964 entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool); 965 966 if (their_old_abspath) 967 entry->conflict_old = svn_dirent_basename(their_old_abspath, 968 result_pool); 969 970 if (their_abspath) 971 entry->conflict_new = svn_dirent_basename(their_abspath, 972 result_pool); 973 } 974 975 if (prop_conflicted) 976 { 977 const char *prej_abspath; 978 979 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL, 980 NULL, NULL, NULL, 981 db, dir_abspath, 982 conflict, scratch_pool, 983 scratch_pool)); 984 985 if (prej_abspath) 986 entry->prejfile = svn_dirent_basename(prej_abspath, result_pool); 987 } 988 } 989 990 if (lock) 991 { 992 entry->lock_token = lock->token; 993 entry->lock_owner = lock->owner; 994 entry->lock_comment = lock->comment; 995 entry->lock_creation_date = lock->date; 996 } 997 998 /* Let's check for a file external. ugh. */ 999 if (status == svn_wc__db_status_normal 1000 && kind == svn_node_file) 1001 SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath, 1002 result_pool, scratch_pool)); 1003 1004 entry->working_size = translated_size; 1005 1006 *new_entry = entry; 1007 1008 return SVN_NO_ERROR; 1009} 1010 1011/* Read entries for PATH/LOCAL_ABSPATH from DB. The entries 1012 will be allocated in RESULT_POOL, with temporary allocations in 1013 SCRATCH_POOL. The entries are returned in RESULT_ENTRIES. */ 1014static svn_error_t * 1015read_entries_new(apr_hash_t **result_entries, 1016 svn_wc__db_t *db, 1017 const char *dir_abspath, 1018 svn_wc__db_wcroot_t *wcroot, 1019 const char *dir_relpath, 1020 apr_pool_t *result_pool, 1021 apr_pool_t *scratch_pool) 1022{ 1023 apr_hash_t *entries; 1024 const apr_array_header_t *children; 1025 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1026 int i; 1027 const svn_wc_entry_t *parent_entry; 1028 1029 entries = apr_hash_make(result_pool); 1030 1031 SVN_ERR(read_one_entry(&parent_entry, 1032 db, dir_abspath, 1033 wcroot, dir_relpath, 1034 "" /* name */, 1035 NULL /* parent_entry */, 1036 result_pool, iterpool)); 1037 svn_hash_sets(entries, "", parent_entry); 1038 1039 /* Use result_pool so that the child names (used by reference, rather 1040 than copied) appear in result_pool. */ 1041 SVN_ERR(svn_wc__db_read_children(&children, db, 1042 dir_abspath, 1043 scratch_pool, iterpool)); 1044 for (i = children->nelts; i--; ) 1045 { 1046 const char *name = APR_ARRAY_IDX(children, i, const char *); 1047 const svn_wc_entry_t *entry; 1048 1049 svn_pool_clear(iterpool); 1050 1051 SVN_ERR(read_one_entry(&entry, 1052 db, dir_abspath, 1053 wcroot, dir_relpath, 1054 name, parent_entry, 1055 result_pool, iterpool)); 1056 svn_hash_sets(entries, entry->name, entry); 1057 } 1058 1059 svn_pool_destroy(iterpool); 1060 1061 *result_entries = entries; 1062 1063 return SVN_NO_ERROR; 1064} 1065 1066 1067static svn_error_t * 1068read_entry_pair_txn(const svn_wc_entry_t **parent_entry, 1069 const svn_wc_entry_t **entry, 1070 svn_wc__db_t *db, 1071 const char *dir_abspath, 1072 svn_wc__db_wcroot_t *wcroot, 1073 const char *dir_relpath, 1074 const char *name, 1075 apr_pool_t *result_pool, 1076 apr_pool_t *scratch_pool) 1077{ 1078 SVN_ERR(read_one_entry(parent_entry, 1079 db, dir_abspath, 1080 wcroot, dir_relpath, 1081 "" /* name */, 1082 NULL /* parent_entry */, 1083 result_pool, scratch_pool)); 1084 1085 /* If we need the entry for "this dir", then return the parent_entry 1086 in both outputs. Otherwise, read the child node. */ 1087 if (*name == '\0') 1088 { 1089 /* If the retrieved node is a FILE, then we have a problem. We asked 1090 for a directory. This implies there is an obstructing, unversioned 1091 directory where a FILE should be. We navigated from the obstructing 1092 subdir up to the parent dir, then returned the FILE found there. 1093 1094 Let's return WC_MISSING cuz the caller thought we had a dir, but 1095 that (versioned subdir) isn't there. */ 1096 if ((*parent_entry)->kind == svn_node_file) 1097 { 1098 *parent_entry = NULL; 1099 return svn_error_createf(SVN_ERR_WC_MISSING, NULL, 1100 _("'%s' is not a versioned working copy"), 1101 svn_dirent_local_style(dir_abspath, 1102 scratch_pool)); 1103 } 1104 1105 *entry = *parent_entry; 1106 } 1107 else 1108 { 1109 const apr_array_header_t *children; 1110 int i; 1111 1112 /* Default to not finding the child. */ 1113 *entry = NULL; 1114 1115 /* Determine whether the parent KNOWS about this child. If it does 1116 not, then we should not attempt to look for it. 1117 1118 For example: the parent doesn't "know" about the child, but the 1119 versioned directory *does* exist on disk. We don't want to look 1120 into that subdir. */ 1121 SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath, 1122 scratch_pool, scratch_pool)); 1123 for (i = children->nelts; i--; ) 1124 { 1125 const char *child = APR_ARRAY_IDX(children, i, const char *); 1126 1127 if (strcmp(child, name) == 0) 1128 { 1129 svn_error_t *err; 1130 1131 err = read_one_entry(entry, 1132 db, dir_abspath, 1133 wcroot, dir_relpath, 1134 name, *parent_entry, 1135 result_pool, scratch_pool); 1136 if (err) 1137 { 1138 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1139 return svn_error_trace(err); 1140 1141 /* No problem. Clear the error and leave the default value 1142 of "missing". */ 1143 svn_error_clear(err); 1144 } 1145 1146 /* Found it. No need to keep searching. */ 1147 break; 1148 } 1149 } 1150 /* if the loop ends without finding a child, then we have the default 1151 ENTRY value of NULL. */ 1152 } 1153 1154 return SVN_NO_ERROR; 1155} 1156 1157/* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return 1158 the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The 1159 two returned pointers will be the same if NAME=="" ("this dir"). 1160 1161 The parent entry must exist. 1162 1163 The requested entry MAY exist. If it does not, then NULL will be returned. 1164 1165 The resulting entries are allocated in RESULT_POOL, and all temporary 1166 allocations are made in SCRATCH_POOL. */ 1167static svn_error_t * 1168read_entry_pair(const svn_wc_entry_t **parent_entry, 1169 const svn_wc_entry_t **entry, 1170 svn_wc__db_t *db, 1171 const char *dir_abspath, 1172 const char *name, 1173 apr_pool_t *result_pool, 1174 apr_pool_t *scratch_pool) 1175{ 1176 svn_wc__db_wcroot_t *wcroot; 1177 const char *dir_relpath; 1178 1179 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, 1180 db, dir_abspath, 1181 scratch_pool, scratch_pool)); 1182 VERIFY_USABLE_WCROOT(wcroot); 1183 1184 SVN_WC__DB_WITH_TXN(read_entry_pair_txn(parent_entry, entry, 1185 db, dir_abspath, 1186 wcroot, dir_relpath, 1187 name, 1188 result_pool, scratch_pool), 1189 wcroot); 1190 1191 return SVN_NO_ERROR; 1192} 1193 1194/* */ 1195static svn_error_t * 1196read_entries(apr_hash_t **entries, 1197 svn_wc__db_t *db, 1198 const char *dir_abspath, 1199 apr_pool_t *result_pool, 1200 apr_pool_t *scratch_pool) 1201{ 1202 svn_wc__db_wcroot_t *wcroot; 1203 const char *dir_relpath; 1204 int wc_format; 1205 1206 SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, dir_abspath, 1207 scratch_pool)); 1208 1209 if (wc_format < SVN_WC__WC_NG_VERSION) 1210 return svn_error_trace(svn_wc__read_entries_old(entries, 1211 dir_abspath, 1212 result_pool, 1213 scratch_pool)); 1214 1215 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, 1216 db, dir_abspath, 1217 scratch_pool, scratch_pool)); 1218 VERIFY_USABLE_WCROOT(wcroot); 1219 1220 SVN_WC__DB_WITH_TXN(read_entries_new(entries, 1221 db, dir_abspath, 1222 wcroot, dir_relpath, 1223 result_pool, scratch_pool), 1224 wcroot); 1225 1226 return SVN_NO_ERROR; 1227} 1228 1229 1230/* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in 1231 which the entry information is located, and *ENTRY_NAME to the entry name 1232 to access that entry. 1233 1234 KIND is as in svn_wc__get_entry(). 1235 1236 Return the results in RESULT_POOL and use SCRATCH_POOL for temporary 1237 allocations. */ 1238static svn_error_t * 1239get_entry_access_info(const char **adm_abspath, 1240 const char **entry_name, 1241 svn_wc__db_t *db, 1242 const char *local_abspath, 1243 svn_node_kind_t kind, 1244 apr_pool_t *result_pool, 1245 apr_pool_t *scratch_pool) 1246{ 1247 svn_wc_adm_access_t *adm_access; 1248 svn_boolean_t read_from_subdir = FALSE; 1249 1250 /* If the caller didn't know the node kind, then stat the path. Maybe 1251 it is really there, and we can speed up the steps below. */ 1252 if (kind == svn_node_unknown) 1253 { 1254 svn_node_kind_t on_disk; 1255 1256 /* Do we already have an access baton for LOCAL_ABSPATH? */ 1257 adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath, 1258 scratch_pool); 1259 if (adm_access) 1260 { 1261 /* Sweet. The node is a directory. */ 1262 on_disk = svn_node_dir; 1263 } 1264 else 1265 { 1266 svn_boolean_t special; 1267 1268 /* What's on disk? */ 1269 SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special, 1270 scratch_pool)); 1271 } 1272 1273 if (on_disk != svn_node_dir) 1274 { 1275 /* If this is *anything* besides a directory (FILE, NONE, or 1276 UNKNOWN), then we cannot treat it as a versioned directory 1277 containing entries to read. Leave READ_FROM_SUBDIR as FALSE, 1278 so that the parent will be examined. 1279 1280 For NONE and UNKNOWN, it may be that metadata exists for the 1281 node, even though on-disk is unhelpful. 1282 1283 If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY, 1284 then we'll error. 1285 1286 If NEED_PARENT_STUB if FALSE, and we successfully read a stub, 1287 then this on-disk node is obstructing the read. */ 1288 } 1289 else 1290 { 1291 /* We found a directory for this UNKNOWN node. Determine whether 1292 we need to read inside it. */ 1293 read_from_subdir = TRUE; 1294 } 1295 } 1296 else if (kind == svn_node_dir) 1297 { 1298 read_from_subdir = TRUE; 1299 } 1300 1301 if (read_from_subdir) 1302 { 1303 /* KIND must be a DIR or UNKNOWN (and we found a subdir). We want 1304 the "real" data, so treat LOCAL_ABSPATH as a versioned directory. */ 1305 *adm_abspath = apr_pstrdup(result_pool, local_abspath); 1306 *entry_name = ""; 1307 } 1308 else 1309 { 1310 /* FILE node needs to read the parent directory. Or a DIR node 1311 needs to read from the parent to get at the stub entry. Or this 1312 is an UNKNOWN node, and we need to examine the parent. */ 1313 svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool); 1314 } 1315 1316 return SVN_NO_ERROR; 1317} 1318 1319 1320svn_error_t * 1321svn_wc__get_entry(const svn_wc_entry_t **entry, 1322 svn_wc__db_t *db, 1323 const char *local_abspath, 1324 svn_boolean_t allow_unversioned, 1325 svn_node_kind_t kind, 1326 apr_pool_t *result_pool, 1327 apr_pool_t *scratch_pool) 1328{ 1329 const char *dir_abspath; 1330 const char *entry_name; 1331 1332 SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath, 1333 kind, scratch_pool, scratch_pool)); 1334 1335 { 1336 const svn_wc_entry_t *parent_entry; 1337 svn_error_t *err; 1338 1339 /* NOTE: if KIND is UNKNOWN and we decided to examine the *parent* 1340 directory, then it is possible we moved out of the working copy. 1341 If the on-disk node is a DIR, and we asked for a stub, then we 1342 obviously can't provide that (parent has no info). If the on-disk 1343 node is a FILE/NONE/UNKNOWN, then it is obstructing the real 1344 LOCAL_ABSPATH (or it was never a versioned item). In all these 1345 cases, the read_entries() will (properly) throw an error. 1346 1347 NOTE: if KIND is a DIR and we asked for the real data, but it is 1348 obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN), 1349 then this will throw an error. */ 1350 1351 err = read_entry_pair(&parent_entry, entry, 1352 db, dir_abspath, entry_name, 1353 result_pool, scratch_pool); 1354 if (err) 1355 { 1356 if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown 1357 || *entry_name != '\0') 1358 return svn_error_trace(err); 1359 svn_error_clear(err); 1360 1361 /* The caller didn't know the node type, we saw a directory there, 1362 we attempted to read IN that directory, and then wc_db reports 1363 that it is NOT a working copy directory. It is possible that 1364 one of two things has happened: 1365 1366 1) a directory is obstructing a file in the parent 1367 2) the (versioned) directory's contents have been removed 1368 1369 Let's assume situation (1); if that is true, then we can just 1370 return the newly-found data. 1371 1372 If we assumed (2), then a valid result still won't help us 1373 since the caller asked for the actual contents, not the stub 1374 (which is why we read *into* the directory). However, if we 1375 assume (1) and get back a stub, then we have verified a 1376 missing, versioned directory, and can return an error 1377 describing that. 1378 1379 Redo the fetch, but "insist" we are trying to find a file. 1380 This will read from the parent directory of the "file". */ 1381 err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned, 1382 svn_node_file, result_pool, scratch_pool); 1383 if (err == SVN_NO_ERROR) 1384 return SVN_NO_ERROR; 1385 if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND) 1386 return svn_error_trace(err); 1387 svn_error_clear(err); 1388 1389 /* We asked for a FILE, but the node found is a DIR. Thus, we 1390 are looking at a stub. Originally, we tried to read into the 1391 subdir because NEED_PARENT_STUB is FALSE. The stub we just 1392 read is not going to work for the caller, so inform them of 1393 the missing subdirectory. */ 1394 SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir); 1395 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 1396 _("Admin area of '%s' is missing"), 1397 svn_dirent_local_style(local_abspath, 1398 scratch_pool)); 1399 } 1400 } 1401 1402 if (*entry == NULL) 1403 { 1404 if (allow_unversioned) 1405 return SVN_NO_ERROR; 1406 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 1407 _("'%s' is not under version control"), 1408 svn_dirent_local_style(local_abspath, 1409 scratch_pool)); 1410 } 1411 1412 /* The caller had the wrong information. */ 1413 if ((kind == svn_node_file && (*entry)->kind != svn_node_file) 1414 || (kind == svn_node_dir && (*entry)->kind != svn_node_dir)) 1415 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 1416 _("'%s' is not of the right kind"), 1417 svn_dirent_local_style(local_abspath, 1418 scratch_pool)); 1419 1420 return SVN_NO_ERROR; 1421} 1422 1423/* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS. 1424 1425 Prune the deleted entries from the cached entries in ADM_ACCESS, and 1426 return that collection in *ENTRIES_PRUNED. SCRATCH_POOL is used for local, 1427 short term, memory allocation, RESULT_POOL for permanent stuff. */ 1428static svn_error_t * 1429prune_deleted(apr_hash_t **entries_pruned, 1430 apr_hash_t *entries_all, 1431 apr_pool_t *result_pool, 1432 apr_pool_t *scratch_pool) 1433{ 1434 apr_hash_index_t *hi; 1435 1436 if (!entries_all) 1437 { 1438 *entries_pruned = NULL; 1439 return SVN_NO_ERROR; 1440 } 1441 1442 /* I think it will be common for there to be no deleted entries, so 1443 it is worth checking for that case as we can optimise it. */ 1444 for (hi = apr_hash_first(scratch_pool, entries_all); 1445 hi; 1446 hi = apr_hash_next(hi)) 1447 { 1448 svn_boolean_t hidden; 1449 1450 SVN_ERR(svn_wc__entry_is_hidden(&hidden, 1451 apr_hash_this_val(hi))); 1452 if (hidden) 1453 break; 1454 } 1455 1456 if (! hi) 1457 { 1458 /* There are no deleted entries, so we can use the full hash */ 1459 *entries_pruned = entries_all; 1460 return SVN_NO_ERROR; 1461 } 1462 1463 /* Construct pruned hash without deleted entries */ 1464 *entries_pruned = apr_hash_make(result_pool); 1465 for (hi = apr_hash_first(scratch_pool, entries_all); 1466 hi; 1467 hi = apr_hash_next(hi)) 1468 { 1469 const void *key = apr_hash_this_key(hi); 1470 const svn_wc_entry_t *entry = apr_hash_this_val(hi); 1471 svn_boolean_t hidden; 1472 1473 SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry)); 1474 if (!hidden) 1475 svn_hash_sets(*entries_pruned, key, entry); 1476 } 1477 1478 return SVN_NO_ERROR; 1479} 1480 1481svn_error_t * 1482svn_wc__entries_read_internal(apr_hash_t **entries, 1483 svn_wc_adm_access_t *adm_access, 1484 svn_boolean_t show_hidden, 1485 apr_pool_t *pool) 1486{ 1487 apr_hash_t *new_entries; 1488 1489 new_entries = svn_wc__adm_access_entries(adm_access); 1490 if (! new_entries) 1491 { 1492 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 1493 const char *local_abspath = svn_wc__adm_access_abspath(adm_access); 1494 apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access); 1495 1496 SVN_ERR(read_entries(&new_entries, db, local_abspath, 1497 result_pool, pool)); 1498 1499 svn_wc__adm_access_set_entries(adm_access, new_entries); 1500 } 1501 1502 if (show_hidden) 1503 *entries = new_entries; 1504 else 1505 SVN_ERR(prune_deleted(entries, new_entries, 1506 svn_wc__adm_access_pool_internal(adm_access), 1507 pool)); 1508 1509 return SVN_NO_ERROR; 1510} 1511 1512svn_error_t * 1513svn_wc_entries_read(apr_hash_t **entries, 1514 svn_wc_adm_access_t *adm_access, 1515 svn_boolean_t show_hidden, 1516 apr_pool_t *pool) 1517{ 1518 return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access, 1519 show_hidden, pool)); 1520} 1521 1522/* No transaction required: called from write_entry which is itself 1523 transaction-wrapped. */ 1524static svn_error_t * 1525insert_node(svn_sqlite__db_t *sdb, 1526 const db_node_t *node, 1527 apr_pool_t *scratch_pool) 1528{ 1529 svn_sqlite__stmt_t *stmt; 1530 svn_boolean_t present = (node->presence == svn_wc__db_status_normal 1531 || node->presence == svn_wc__db_status_incomplete); 1532 1533 SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath); 1534 1535 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE)); 1536 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsn", 1537 node->wc_id, 1538 node->local_relpath, 1539 node->op_depth, 1540 node->parent_relpath, 1541 /* Setting depth for files? */ 1542 (node->kind == svn_node_dir && present) 1543 ? svn_depth_to_word(node->depth) 1544 : NULL)); 1545 1546 if (present && node->repos_relpath) 1547 { 1548 SVN_ERR(svn_sqlite__bind_revnum(stmt, 11, node->changed_rev)); 1549 SVN_ERR(svn_sqlite__bind_int64(stmt, 12, node->changed_date)); 1550 SVN_ERR(svn_sqlite__bind_text(stmt, 13, node->changed_author)); 1551 } 1552 1553 if (node->repos_relpath 1554 && node->presence != svn_wc__db_status_base_deleted) 1555 { 1556 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, 1557 node->repos_id)); 1558 SVN_ERR(svn_sqlite__bind_text(stmt, 6, 1559 node->repos_relpath)); 1560 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision)); 1561 } 1562 1563 SVN_ERR(svn_sqlite__bind_token(stmt, 8, presence_map, node->presence)); 1564 1565 if (node->kind == svn_node_none) 1566 SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown")); 1567 else 1568 SVN_ERR(svn_sqlite__bind_token(stmt, 10, kind_map, node->kind)); 1569 1570 if (node->kind == svn_node_file && present) 1571 { 1572 if (!node->checksum 1573 && node->op_depth == 0 1574 && node->presence != svn_wc__db_status_not_present 1575 && node->presence != svn_wc__db_status_excluded 1576 && node->presence != svn_wc__db_status_server_excluded) 1577 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 1578 _("The file '%s' has no checksum"), 1579 svn_dirent_local_style(node->local_relpath, 1580 scratch_pool)); 1581 1582 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum, 1583 scratch_pool)); 1584 1585 if (node->repos_relpath) 1586 { 1587 if (node->recorded_size != SVN_INVALID_FILESIZE) 1588 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size)); 1589 1590 SVN_ERR(svn_sqlite__bind_int64(stmt, 17, node->recorded_time)); 1591 } 1592 } 1593 1594 /* ### Never set, props done later */ 1595 if (node->properties && present && node->repos_relpath) 1596 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties, 1597 scratch_pool)); 1598 1599 if (node->file_external) 1600 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1)); 1601 1602 if (node->inherited_props && present) 1603 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props, 1604 scratch_pool)); 1605 1606 SVN_ERR(svn_sqlite__insert(NULL, stmt)); 1607 1608 return SVN_NO_ERROR; 1609} 1610 1611 1612/* */ 1613static svn_error_t * 1614insert_actual_node(svn_sqlite__db_t *sdb, 1615 svn_wc__db_t *db, 1616 const char *wri_abspath, 1617 const db_actual_node_t *actual_node, 1618 apr_pool_t *scratch_pool) 1619{ 1620 svn_sqlite__stmt_t *stmt; 1621 svn_skel_t *conflict_data = NULL; 1622 1623 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE)); 1624 1625 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id)); 1626 SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath)); 1627 SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath)); 1628 1629 if (actual_node->properties) 1630 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties, 1631 scratch_pool)); 1632 1633 if (actual_node->changelist) 1634 SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist)); 1635 1636 SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw( 1637 &conflict_data, 1638 db, wri_abspath, 1639 actual_node->local_relpath, 1640 actual_node->conflict_old, 1641 actual_node->conflict_working, 1642 actual_node->conflict_new, 1643 actual_node->prop_reject, 1644 actual_node->tree_conflict_data, 1645 actual_node->tree_conflict_data 1646 ? strlen(actual_node->tree_conflict_data) 1647 : 0, 1648 scratch_pool, scratch_pool)); 1649 1650 if (conflict_data) 1651 { 1652 svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool); 1653 1654 SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len)); 1655 } 1656 1657 /* Execute and reset the insert clause. */ 1658 return svn_error_trace(svn_sqlite__insert(NULL, stmt)); 1659} 1660 1661static svn_boolean_t 1662is_switched(db_node_t *parent, 1663 db_node_t *child, 1664 apr_pool_t *scratch_pool) 1665{ 1666 if (parent && child) 1667 { 1668 if (parent->repos_id != child->repos_id) 1669 return TRUE; 1670 1671 if (parent->repos_relpath && child->repos_relpath) 1672 { 1673 const char *unswitched 1674 = svn_relpath_join(parent->repos_relpath, 1675 svn_relpath_basename(child->local_relpath, 1676 scratch_pool), 1677 scratch_pool); 1678 if (strcmp(unswitched, child->repos_relpath)) 1679 return TRUE; 1680 } 1681 } 1682 1683 return FALSE; 1684} 1685 1686struct write_baton { 1687 db_node_t *base; 1688 db_node_t *work; 1689 db_node_t *below_work; 1690 apr_hash_t *tree_conflicts; 1691}; 1692 1693#define WRITE_ENTRY_ASSERT(expr) \ 1694 if (!(expr)) \ 1695 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, \ 1696 _("Unable to upgrade '%s' at line %d"), \ 1697 svn_dirent_local_style( \ 1698 svn_dirent_join(root_abspath, \ 1699 local_relpath, \ 1700 scratch_pool), \ 1701 scratch_pool), __LINE__) 1702 1703/* Write the information for ENTRY to WC_DB. The WC_ID, REPOS_ID and 1704 REPOS_ROOT will all be used for writing ENTRY. 1705 ### transitioning from straight sql to using the wc_db APIs. For the 1706 ### time being, we'll need both parameters. */ 1707static svn_error_t * 1708write_entry(struct write_baton **entry_node, 1709 const struct write_baton *parent_node, 1710 svn_wc__db_t *db, 1711 svn_sqlite__db_t *sdb, 1712 apr_int64_t wc_id, 1713 apr_int64_t repos_id, 1714 const svn_wc_entry_t *entry, 1715 const svn_wc__text_base_info_t *text_base_info, 1716 const char *local_relpath, 1717 const char *tmp_entry_abspath, 1718 const char *root_abspath, 1719 const svn_wc_entry_t *this_dir, 1720 svn_boolean_t create_locks, 1721 apr_pool_t *result_pool, 1722 apr_pool_t *scratch_pool) 1723{ 1724 db_node_t *base_node = NULL; 1725 db_node_t *working_node = NULL, *below_working_node = NULL; 1726 db_actual_node_t *actual_node = NULL; 1727 const char *parent_relpath; 1728 apr_hash_t *tree_conflicts; 1729 1730 if (*local_relpath == '\0') 1731 parent_relpath = NULL; 1732 else 1733 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool); 1734 1735 /* This is how it should work, it doesn't work like this yet because 1736 we need proper op_depth to layer the working nodes. 1737 1738 Using "svn add", "svn rm", "svn cp" only files can be replaced 1739 pre-wcng; directories can only be normal, deleted or added. 1740 Files cannot be replaced within a deleted directory, so replaced 1741 files can only exist in a normal directory, or a directory that 1742 is added+copied. In a normal directory a replaced file needs a 1743 base node and a working node, in an added+copied directory a 1744 replaced file needs two working nodes at different op-depths. 1745 1746 With just the above operations the conversion for files and 1747 directories is straightforward: 1748 1749 pre-wcng wcng 1750 parent child parent child 1751 1752 normal normal base base 1753 add+copied normal+copied work work 1754 normal+copied normal+copied work work 1755 normal delete base base+work 1756 delete delete base+work base+work 1757 add+copied delete work work 1758 normal add base work 1759 add add work work 1760 add+copied add work work 1761 normal add+copied base work 1762 add add+copied work work 1763 add+copied add+copied work work 1764 normal replace base base+work 1765 add+copied replace work work+work 1766 normal replace+copied base base+work 1767 add+copied replace+copied work work+work 1768 1769 However "svn merge" make this more complicated. The pre-wcng 1770 "svn merge" is capable of replacing a directory, that is it can 1771 mark the whole tree deleted, and then copy another tree on top. 1772 The entries then represent the replacing tree overlayed on the 1773 deleted tree. 1774 1775 original replace schedule in 1776 tree tree combined tree 1777 1778 A A replace+copied 1779 A/f delete+copied 1780 A/g A/g replace+copied 1781 A/h add+copied 1782 A/B A/B replace+copied 1783 A/B/f delete+copied 1784 A/B/g A/B/g replace+copied 1785 A/B/h add+copied 1786 A/C delete+copied 1787 A/C/f delete+copied 1788 A/D add+copied 1789 A/D/f add+copied 1790 1791 The original tree could be normal tree, or an add+copied tree. 1792 Committing such a merge generally worked, but making further tree 1793 modifications before commit sometimes failed. 1794 1795 The root of the replace is handled like the file replace: 1796 1797 pre-wcng wcng 1798 parent child parent child 1799 1800 normal replace+copied base base+work 1801 add+copied replace+copied work work+work 1802 1803 although obviously the node is a directory rather then a file. 1804 There are then more conversion states where the parent is 1805 replaced. 1806 1807 pre-wcng wcng 1808 parent child parent child 1809 1810 replace+copied add [base|work]+work work 1811 replace+copied add+copied [base|work]+work work 1812 replace+copied delete+copied [base|work]+work [base|work]+work 1813 delete+copied delete+copied [base|work]+work [base|work]+work 1814 replace+copied replace+copied [base|work]+work [base|work]+work 1815 */ 1816 1817 WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal); 1818 1819 WRITE_ENTRY_ASSERT(!parent_node || parent_node->base 1820 || parent_node->below_work || parent_node->work); 1821 1822 switch (entry->schedule) 1823 { 1824 case svn_wc_schedule_normal: 1825 if (entry->copied || 1826 (entry->depth == svn_depth_exclude 1827 && parent_node && !parent_node->base && parent_node->work)) 1828 working_node = MAYBE_ALLOC(working_node, result_pool); 1829 else 1830 base_node = MAYBE_ALLOC(base_node, result_pool); 1831 break; 1832 1833 case svn_wc_schedule_add: 1834 working_node = MAYBE_ALLOC(working_node, result_pool); 1835 if (entry->deleted) 1836 { 1837 if (parent_node->base) 1838 base_node = MAYBE_ALLOC(base_node, result_pool); 1839 else 1840 below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1841 } 1842 break; 1843 1844 case svn_wc_schedule_delete: 1845 working_node = MAYBE_ALLOC(working_node, result_pool); 1846 if (parent_node->base) 1847 base_node = MAYBE_ALLOC(base_node, result_pool); 1848 if (parent_node->work) 1849 below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1850 break; 1851 1852 case svn_wc_schedule_replace: 1853 working_node = MAYBE_ALLOC(working_node, result_pool); 1854 if (parent_node->base) 1855 base_node = MAYBE_ALLOC(base_node, result_pool); 1856 else 1857 below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1858 break; 1859 } 1860 1861 /* Something deleted in this revision means there should always be a 1862 BASE node to indicate the not-present node. */ 1863 if (entry->deleted) 1864 { 1865 WRITE_ENTRY_ASSERT(base_node || below_working_node); 1866 WRITE_ENTRY_ASSERT(!entry->incomplete); 1867 if (base_node) 1868 base_node->presence = svn_wc__db_status_not_present; 1869 else 1870 below_working_node->presence = svn_wc__db_status_not_present; 1871 } 1872 else if (entry->absent) 1873 { 1874 WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node); 1875 WRITE_ENTRY_ASSERT(!entry->incomplete); 1876 base_node->presence = svn_wc__db_status_server_excluded; 1877 } 1878 1879 if (entry->copied) 1880 { 1881 db_node_t *work = parent_node->work 1882 ? parent_node->work 1883 : parent_node->below_work; 1884 1885 if (entry->copyfrom_url) 1886 { 1887 working_node->repos_id = repos_id; 1888 working_node->repos_relpath = svn_uri_skip_ancestor( 1889 this_dir->repos, entry->copyfrom_url, 1890 result_pool); 1891 working_node->revision = entry->copyfrom_rev; 1892 working_node->op_depth 1893 = svn_wc__db_op_depth_for_upgrade(local_relpath); 1894 1895 if (work && work->repos_relpath 1896 && work->repos_id == repos_id 1897 && work->revision == entry->copyfrom_rev) 1898 { 1899 const char *name; 1900 1901 name = svn_relpath_skip_ancestor(work->repos_relpath, 1902 working_node->repos_relpath); 1903 1904 if (name 1905 && !strcmp(name, svn_relpath_basename(local_relpath, NULL))) 1906 { 1907 working_node->op_depth = work->op_depth; 1908 } 1909 } 1910 } 1911 else if (work && work->repos_relpath) 1912 { 1913 working_node->repos_id = repos_id; 1914 working_node->repos_relpath 1915 = svn_relpath_join(work->repos_relpath, 1916 svn_relpath_basename(local_relpath, NULL), 1917 result_pool); 1918 working_node->revision = work->revision; 1919 working_node->op_depth = work->op_depth; 1920 } 1921 else if (parent_node->below_work 1922 && parent_node->below_work->repos_relpath) 1923 { 1924 /* Parent deleted, this not-present or similar */ 1925 working_node->repos_id = repos_id; 1926 working_node->repos_relpath 1927 = svn_relpath_join(parent_node->below_work->repos_relpath, 1928 svn_relpath_basename(local_relpath, NULL), 1929 result_pool); 1930 working_node->revision = parent_node->below_work->revision; 1931 working_node->op_depth = parent_node->below_work->op_depth; 1932 } 1933 else 1934 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, 1935 _("No copyfrom URL for '%s'"), 1936 svn_dirent_local_style(local_relpath, 1937 scratch_pool)); 1938 1939 if (work && work->op_depth != working_node->op_depth 1940 && work->repos_relpath 1941 && work->repos_id == working_node->repos_id 1942 && work->presence == svn_wc__db_status_normal 1943 && !below_working_node) 1944 { 1945 /* Introduce a not-present node! */ 1946 below_working_node = MAYBE_ALLOC(below_working_node, scratch_pool); 1947 1948 below_working_node->wc_id = wc_id; 1949 below_working_node->op_depth = work->op_depth; 1950 below_working_node->local_relpath = local_relpath; 1951 below_working_node->parent_relpath = parent_relpath; 1952 1953 below_working_node->presence = svn_wc__db_status_not_present; 1954 below_working_node->repos_id = repos_id; 1955 below_working_node->repos_relpath = working_node->local_relpath; 1956 1957 SVN_ERR(insert_node(sdb, below_working_node, scratch_pool)); 1958 1959 below_working_node = NULL; /* Don't write a present intermediate! */ 1960 } 1961 } 1962 1963 if (entry->conflict_old) 1964 { 1965 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1966 if (parent_relpath && entry->conflict_old) 1967 actual_node->conflict_old = svn_relpath_join(parent_relpath, 1968 entry->conflict_old, 1969 scratch_pool); 1970 else 1971 actual_node->conflict_old = entry->conflict_old; 1972 if (parent_relpath && entry->conflict_new) 1973 actual_node->conflict_new = svn_relpath_join(parent_relpath, 1974 entry->conflict_new, 1975 scratch_pool); 1976 else 1977 actual_node->conflict_new = entry->conflict_new; 1978 if (parent_relpath && entry->conflict_wrk) 1979 actual_node->conflict_working = svn_relpath_join(parent_relpath, 1980 entry->conflict_wrk, 1981 scratch_pool); 1982 else 1983 actual_node->conflict_working = entry->conflict_wrk; 1984 } 1985 1986 if (entry->prejfile) 1987 { 1988 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1989 actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir 1990 ? local_relpath 1991 : parent_relpath), 1992 entry->prejfile, 1993 scratch_pool); 1994 } 1995 1996 if (entry->changelist) 1997 { 1998 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1999 actual_node->changelist = entry->changelist; 2000 } 2001 2002 /* ### set the text_mod value? */ 2003 2004 if (entry_node && entry->tree_conflict_data) 2005 { 2006 /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the 2007 parent node, 1.7 stores them directly on the conflited nodes. 2008 So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */ 2009 svn_skel_t *skel; 2010 2011 skel = svn_skel__parse(entry->tree_conflict_data, 2012 strlen(entry->tree_conflict_data), 2013 scratch_pool); 2014 tree_conflicts = apr_hash_make(result_pool); 2015 skel = skel->children; 2016 while (skel) 2017 { 2018 svn_wc_conflict_description2_t *conflict; 2019 svn_skel_t *new_skel; 2020 const char *key; 2021 2022 /* *CONFLICT is allocated so it is safe to use a non-const pointer */ 2023 SVN_ERR(svn_wc__deserialize_conflict( 2024 (const svn_wc_conflict_description2_t**)&conflict, 2025 skel, 2026 svn_dirent_join(root_abspath, 2027 local_relpath, 2028 scratch_pool), 2029 scratch_pool, scratch_pool)); 2030 2031 WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree); 2032 2033 SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict, 2034 scratch_pool, scratch_pool)); 2035 2036 /* Store in hash to be retrieved when writing the child 2037 row. */ 2038 key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath); 2039 svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key), 2040 svn_skel__unparse(new_skel, result_pool)->data); 2041 skel = skel->next; 2042 } 2043 } 2044 else 2045 tree_conflicts = NULL; 2046 2047 if (parent_node && parent_node->tree_conflicts) 2048 { 2049 const char *tree_conflict_data = 2050 svn_hash_gets(parent_node->tree_conflicts, local_relpath); 2051 if (tree_conflict_data) 2052 { 2053 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 2054 actual_node->tree_conflict_data = tree_conflict_data; 2055 } 2056 2057 /* Reset hash so that we don't write the row again when writing 2058 actual-only nodes */ 2059 svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL); 2060 } 2061 2062 if (entry->file_external_path != NULL) 2063 { 2064 base_node = MAYBE_ALLOC(base_node, result_pool); 2065 } 2066 2067 2068 /* Insert the base node. */ 2069 if (base_node) 2070 { 2071 base_node->wc_id = wc_id; 2072 base_node->local_relpath = local_relpath; 2073 base_node->op_depth = 0; 2074 base_node->parent_relpath = parent_relpath; 2075 base_node->revision = entry->revision; 2076 base_node->recorded_time = entry->text_time; 2077 base_node->recorded_size = entry->working_size; 2078 2079 if (entry->depth != svn_depth_exclude) 2080 base_node->depth = entry->depth; 2081 else 2082 { 2083 base_node->presence = svn_wc__db_status_excluded; 2084 base_node->depth = svn_depth_infinity; 2085 } 2086 2087 if (entry->deleted) 2088 { 2089 WRITE_ENTRY_ASSERT(base_node->presence 2090 == svn_wc__db_status_not_present); 2091 /* ### should be svn_node_unknown, but let's store what we have. */ 2092 base_node->kind = entry->kind; 2093 } 2094 else if (entry->absent) 2095 { 2096 WRITE_ENTRY_ASSERT(base_node->presence 2097 == svn_wc__db_status_server_excluded); 2098 /* ### should be svn_node_unknown, but let's store what we have. */ 2099 base_node->kind = entry->kind; 2100 2101 /* Store the most likely revision in the node to avoid 2102 base nodes without a valid revision. Of course 2103 we remember that the data is still incomplete. */ 2104 if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base) 2105 base_node->revision = parent_node->base->revision; 2106 } 2107 else 2108 { 2109 base_node->kind = entry->kind; 2110 2111 if (base_node->presence != svn_wc__db_status_excluded) 2112 { 2113 /* All subdirs are initially incomplete, they stop being 2114 incomplete when the entries file in the subdir is 2115 upgraded and remain incomplete if that doesn't happen. */ 2116 if (entry->kind == svn_node_dir 2117 && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR)) 2118 { 2119 base_node->presence = svn_wc__db_status_incomplete; 2120 2121 /* Store the most likely revision in the node to avoid 2122 base nodes without a valid revision. Of course 2123 we remember that the data is still incomplete. */ 2124 if (parent_node->base) 2125 base_node->revision = parent_node->base->revision; 2126 } 2127 else if (entry->incomplete) 2128 { 2129 /* ### nobody should have set the presence. */ 2130 WRITE_ENTRY_ASSERT(base_node->presence 2131 == svn_wc__db_status_normal); 2132 base_node->presence = svn_wc__db_status_incomplete; 2133 } 2134 } 2135 } 2136 2137 if (entry->kind == svn_node_dir) 2138 base_node->checksum = NULL; 2139 else 2140 { 2141 if (text_base_info && text_base_info->revert_base.sha1_checksum) 2142 base_node->checksum = text_base_info->revert_base.sha1_checksum; 2143 else if (text_base_info && text_base_info->normal_base.sha1_checksum) 2144 base_node->checksum = text_base_info->normal_base.sha1_checksum; 2145 else 2146 base_node->checksum = NULL; 2147 2148 /* The base MD5 checksum is available in the entry, unless there 2149 * is a copied WORKING node. If possible, verify that the entry 2150 * checksum matches the base file that we found. */ 2151 if (! (working_node && entry->copied)) 2152 { 2153 svn_checksum_t *entry_md5_checksum, *found_md5_checksum; 2154 SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum, 2155 svn_checksum_md5, 2156 entry->checksum, scratch_pool)); 2157 if (text_base_info && text_base_info->revert_base.md5_checksum) 2158 found_md5_checksum = text_base_info->revert_base.md5_checksum; 2159 else if (text_base_info 2160 && text_base_info->normal_base.md5_checksum) 2161 found_md5_checksum = text_base_info->normal_base.md5_checksum; 2162 else 2163 found_md5_checksum = NULL; 2164 if (entry_md5_checksum && found_md5_checksum && 2165 !svn_checksum_match(entry_md5_checksum, found_md5_checksum)) 2166 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 2167 _("Bad base MD5 checksum for '%s'; " 2168 "expected: '%s'; found '%s'; "), 2169 svn_dirent_local_style( 2170 svn_dirent_join(root_abspath, 2171 local_relpath, 2172 scratch_pool), 2173 scratch_pool), 2174 svn_checksum_to_cstring_display( 2175 entry_md5_checksum, scratch_pool), 2176 svn_checksum_to_cstring_display( 2177 found_md5_checksum, scratch_pool)); 2178 else 2179 { 2180 /* ### Not sure what conditions this should cover. */ 2181 /* SVN_ERR_ASSERT(entry->deleted || ...); */ 2182 } 2183 } 2184 } 2185 2186 if (this_dir->repos) 2187 { 2188 base_node->repos_id = repos_id; 2189 2190 if (entry->url != NULL) 2191 { 2192 base_node->repos_relpath = svn_uri_skip_ancestor( 2193 this_dir->repos, entry->url, 2194 result_pool); 2195 } 2196 else 2197 { 2198 const char *relpath = svn_uri_skip_ancestor(this_dir->repos, 2199 this_dir->url, 2200 scratch_pool); 2201 if (relpath == NULL || *relpath == '\0') 2202 base_node->repos_relpath = entry->name; 2203 else 2204 base_node->repos_relpath = 2205 svn_dirent_join(relpath, entry->name, result_pool); 2206 } 2207 } 2208 2209 /* TODO: These values should always be present, if they are missing 2210 during an upgrade, set a flag, and then ask the user to talk to the 2211 server. 2212 2213 Note: cmt_rev is the distinguishing value. The others may be 0 or 2214 NULL if the corresponding revprop has been deleted. */ 2215 base_node->changed_rev = entry->cmt_rev; 2216 base_node->changed_date = entry->cmt_date; 2217 base_node->changed_author = entry->cmt_author; 2218 2219 if (entry->file_external_path) 2220 base_node->file_external = TRUE; 2221 2222 /* Switched nodes get an empty iprops cache. */ 2223 if (parent_node 2224 && is_switched(parent_node->base, base_node, scratch_pool)) 2225 base_node->inherited_props 2226 = apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*)); 2227 2228 SVN_ERR(insert_node(sdb, base_node, scratch_pool)); 2229 2230 /* We have to insert the lock after the base node, because the node 2231 must exist to lookup various bits of repos related information for 2232 the abs path. */ 2233 if (entry->lock_token && create_locks) 2234 { 2235 svn_wc__db_lock_t lock; 2236 2237 lock.token = entry->lock_token; 2238 lock.owner = entry->lock_owner; 2239 lock.comment = entry->lock_comment; 2240 lock.date = entry->lock_creation_date; 2241 2242 SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock, 2243 scratch_pool)); 2244 } 2245 } 2246 2247 if (below_working_node) 2248 { 2249 db_node_t *work 2250 = parent_node->below_work ? parent_node->below_work : parent_node->work; 2251 2252 below_working_node->wc_id = wc_id; 2253 below_working_node->local_relpath = local_relpath; 2254 below_working_node->op_depth = work->op_depth; 2255 below_working_node->parent_relpath = parent_relpath; 2256 below_working_node->presence = svn_wc__db_status_normal; 2257 below_working_node->kind = entry->kind; 2258 below_working_node->repos_id = work->repos_id; 2259 below_working_node->revision = work->revision; 2260 2261 /* This is just guessing. If the node below would have been switched 2262 or if it was updated to a different version, the guess would 2263 fail. But we don't have better information pre wc-ng :( */ 2264 if (work->repos_relpath) 2265 below_working_node->repos_relpath 2266 = svn_relpath_join(work->repos_relpath, 2267 svn_relpath_basename(local_relpath, NULL), 2268 result_pool); 2269 else 2270 below_working_node->repos_relpath = NULL; 2271 2272 /* The revert_base checksum isn't available in the entry structure, 2273 so the caller provides it. */ 2274 2275 /* text_base_info is NULL for files scheduled to be added. */ 2276 below_working_node->checksum = NULL; 2277 if (text_base_info) 2278 { 2279 if (entry->schedule == svn_wc_schedule_delete) 2280 below_working_node->checksum = 2281 text_base_info->normal_base.sha1_checksum; 2282 else 2283 below_working_node->checksum = 2284 text_base_info->revert_base.sha1_checksum; 2285 } 2286 below_working_node->recorded_size = 0; 2287 below_working_node->changed_rev = SVN_INVALID_REVNUM; 2288 below_working_node->changed_date = 0; 2289 below_working_node->changed_author = NULL; 2290 below_working_node->depth = svn_depth_infinity; 2291 below_working_node->recorded_time = 0; 2292 below_working_node->properties = NULL; 2293 2294 if (working_node 2295 && entry->schedule == svn_wc_schedule_delete 2296 && working_node->repos_relpath) 2297 { 2298 /* We are lucky, our guesses above are not necessary. The known 2299 correct information is in working. But our op_depth design 2300 expects more information here */ 2301 below_working_node->repos_relpath = working_node->repos_relpath; 2302 below_working_node->repos_id = working_node->repos_id; 2303 below_working_node->revision = working_node->revision; 2304 2305 /* Nice for 'svn status' */ 2306 below_working_node->changed_rev = entry->cmt_rev; 2307 below_working_node->changed_date = entry->cmt_date; 2308 below_working_node->changed_author = entry->cmt_author; 2309 2310 /* And now remove it from WORKING, because in wc-ng code 2311 should read it from the lower layer */ 2312 working_node->repos_relpath = NULL; 2313 working_node->repos_id = 0; 2314 working_node->revision = SVN_INVALID_REVNUM; 2315 } 2316 2317 SVN_ERR(insert_node(sdb, below_working_node, scratch_pool)); 2318 } 2319 2320 /* Insert the working node. */ 2321 if (working_node) 2322 { 2323 working_node->wc_id = wc_id; 2324 working_node->local_relpath = local_relpath; 2325 working_node->parent_relpath = parent_relpath; 2326 working_node->changed_rev = SVN_INVALID_REVNUM; 2327 working_node->recorded_time = entry->text_time; 2328 working_node->recorded_size = entry->working_size; 2329 2330 if (entry->depth != svn_depth_exclude) 2331 working_node->depth = entry->depth; 2332 else 2333 { 2334 working_node->presence = svn_wc__db_status_excluded; 2335 working_node->depth = svn_depth_infinity; 2336 } 2337 2338 if (entry->kind == svn_node_dir) 2339 working_node->checksum = NULL; 2340 else 2341 { 2342 working_node->checksum = NULL; 2343 /* text_base_info is NULL for files scheduled to be added. */ 2344 if (text_base_info) 2345 working_node->checksum = text_base_info->normal_base.sha1_checksum; 2346 2347 2348 /* If an MD5 checksum is present in the entry, we can verify that 2349 * it matches the MD5 of the base file we found earlier. */ 2350#ifdef SVN_DEBUG 2351 if (entry->checksum && text_base_info) 2352 { 2353 svn_checksum_t *md5_checksum; 2354 SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5, 2355 entry->checksum, result_pool)); 2356 SVN_ERR_ASSERT( 2357 md5_checksum && text_base_info->normal_base.md5_checksum); 2358 SVN_ERR_ASSERT(svn_checksum_match( 2359 md5_checksum, text_base_info->normal_base.md5_checksum)); 2360 } 2361#endif 2362 } 2363 2364 working_node->kind = entry->kind; 2365 if (working_node->presence != svn_wc__db_status_excluded) 2366 { 2367 /* All subdirs start of incomplete, and stop being incomplete 2368 when the entries file in the subdir is upgraded. */ 2369 if (entry->kind == svn_node_dir 2370 && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR)) 2371 { 2372 working_node->presence = svn_wc__db_status_incomplete; 2373 working_node->kind = svn_node_dir; 2374 } 2375 else if (entry->schedule == svn_wc_schedule_delete) 2376 { 2377 working_node->presence = svn_wc__db_status_base_deleted; 2378 working_node->kind = entry->kind; 2379 } 2380 else 2381 { 2382 /* presence == normal */ 2383 working_node->kind = entry->kind; 2384 2385 if (entry->incomplete) 2386 { 2387 /* We shouldn't be overwriting another status. */ 2388 WRITE_ENTRY_ASSERT(working_node->presence 2389 == svn_wc__db_status_normal); 2390 working_node->presence = svn_wc__db_status_incomplete; 2391 } 2392 } 2393 } 2394 2395 /* These should generally be unset for added and deleted files, 2396 and contain whatever information we have for copied files. Let's 2397 just store whatever we have. 2398 2399 Note: cmt_rev is the distinguishing value. The others may be 0 or 2400 NULL if the corresponding revprop has been deleted. */ 2401 if (working_node->presence != svn_wc__db_status_base_deleted) 2402 { 2403 working_node->changed_rev = entry->cmt_rev; 2404 working_node->changed_date = entry->cmt_date; 2405 working_node->changed_author = entry->cmt_author; 2406 } 2407 2408 if (entry->schedule == svn_wc_schedule_delete 2409 && parent_node->work 2410 && parent_node->work->presence == svn_wc__db_status_base_deleted) 2411 { 2412 working_node->op_depth = parent_node->work->op_depth; 2413 } 2414 else if (working_node->presence == svn_wc__db_status_excluded 2415 && parent_node->work) 2416 { 2417 working_node->op_depth = parent_node->work->op_depth; 2418 } 2419 else if (!entry->copied) 2420 { 2421 working_node->op_depth 2422 = svn_wc__db_op_depth_for_upgrade(local_relpath); 2423 } 2424 2425 SVN_ERR(insert_node(sdb, working_node, scratch_pool)); 2426 } 2427 2428 /* Insert the actual node. */ 2429 if (actual_node) 2430 { 2431 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 2432 2433 actual_node->wc_id = wc_id; 2434 actual_node->local_relpath = local_relpath; 2435 actual_node->parent_relpath = parent_relpath; 2436 2437 SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath, 2438 actual_node, scratch_pool)); 2439 } 2440 2441 if (entry_node) 2442 { 2443 *entry_node = apr_palloc(result_pool, sizeof(**entry_node)); 2444 (*entry_node)->base = base_node; 2445 (*entry_node)->work = working_node; 2446 (*entry_node)->below_work = below_working_node; 2447 (*entry_node)->tree_conflicts = tree_conflicts; 2448 } 2449 2450 if (entry->file_external_path) 2451 { 2452 /* TODO: Maybe add a file external registration inside EXTERNALS here, 2453 to allow removing file externals that aren't referenced from 2454 svn:externals. 2455 2456 The svn:externals values are processed anyway after everything is 2457 upgraded */ 2458 } 2459 2460 return SVN_NO_ERROR; 2461} 2462 2463static svn_error_t * 2464write_actual_only_entries(apr_hash_t *tree_conflicts, 2465 svn_sqlite__db_t *sdb, 2466 svn_wc__db_t *db, 2467 const char *wri_abspath, 2468 apr_int64_t wc_id, 2469 const char *parent_relpath, 2470 apr_pool_t *scratch_pool) 2471{ 2472 apr_hash_index_t *hi; 2473 2474 for (hi = apr_hash_first(scratch_pool, tree_conflicts); 2475 hi; 2476 hi = apr_hash_next(hi)) 2477 { 2478 db_actual_node_t *actual_node = NULL; 2479 2480 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 2481 actual_node->wc_id = wc_id; 2482 actual_node->local_relpath = apr_hash_this_key(hi); 2483 actual_node->parent_relpath = parent_relpath; 2484 actual_node->tree_conflict_data = apr_hash_this_val(hi); 2485 2486 SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node, 2487 scratch_pool)); 2488 } 2489 2490 return SVN_NO_ERROR; 2491} 2492 2493svn_error_t * 2494svn_wc__write_upgraded_entries(void **dir_baton, 2495 void *parent_baton, 2496 svn_wc__db_t *db, 2497 svn_sqlite__db_t *sdb, 2498 apr_int64_t repos_id, 2499 apr_int64_t wc_id, 2500 const char *dir_abspath, 2501 const char *new_root_abspath, 2502 apr_hash_t *entries, 2503 apr_hash_t *text_bases_info, 2504 apr_pool_t *result_pool, 2505 apr_pool_t *scratch_pool) 2506{ 2507 const svn_wc_entry_t *this_dir; 2508 apr_hash_index_t *hi; 2509 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2510 const char *old_root_abspath, *dir_relpath; 2511 struct write_baton *parent_node = parent_baton; 2512 struct write_baton *dir_node; 2513 2514 /* Get a copy of the "this dir" entry for comparison purposes. */ 2515 this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 2516 2517 /* If there is no "this dir" entry, something is wrong. */ 2518 if (! this_dir) 2519 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 2520 _("No default entry in directory '%s'"), 2521 svn_dirent_local_style(dir_abspath, 2522 iterpool)); 2523 old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath, 2524 new_root_abspath, 2525 scratch_pool); 2526 2527 SVN_ERR_ASSERT(old_root_abspath[0]); 2528 2529 dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath); 2530 2531 /* Write out "this dir" */ 2532 SVN_ERR(write_entry(&dir_node, parent_node, db, sdb, 2533 wc_id, repos_id, this_dir, NULL, dir_relpath, 2534 svn_dirent_join(new_root_abspath, dir_relpath, 2535 iterpool), 2536 old_root_abspath, 2537 this_dir, FALSE, result_pool, iterpool)); 2538 2539 for (hi = apr_hash_first(scratch_pool, entries); hi; 2540 hi = apr_hash_next(hi)) 2541 { 2542 const char *name = apr_hash_this_key(hi); 2543 const svn_wc_entry_t *this_entry = apr_hash_this_val(hi); 2544 const char *child_abspath, *child_relpath; 2545 svn_wc__text_base_info_t *text_base_info 2546 = svn_hash_gets(text_bases_info, name); 2547 2548 svn_pool_clear(iterpool); 2549 2550 /* Don't rewrite the "this dir" entry! */ 2551 if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0) 2552 continue; 2553 2554 /* Write the entry. Pass TRUE for create locks, because we still 2555 use this function for upgrading old working copies. */ 2556 child_abspath = svn_dirent_join(dir_abspath, name, iterpool); 2557 child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath); 2558 SVN_ERR(write_entry(NULL, dir_node, db, sdb, 2559 wc_id, repos_id, 2560 this_entry, text_base_info, child_relpath, 2561 svn_dirent_join(new_root_abspath, child_relpath, 2562 iterpool), 2563 old_root_abspath, 2564 this_dir, TRUE, iterpool, iterpool)); 2565 } 2566 2567 if (dir_node->tree_conflicts) 2568 SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db, 2569 new_root_abspath, wc_id, dir_relpath, 2570 iterpool)); 2571 2572 *dir_baton = dir_node; 2573 svn_pool_destroy(iterpool); 2574 return SVN_NO_ERROR; 2575} 2576 2577 2578svn_wc_entry_t * 2579svn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool) 2580{ 2581 svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry)); 2582 2583 /* Perform a trivial copy ... */ 2584 *dupentry = *entry; 2585 2586 /* ...and then re-copy stuff that needs to be duped into our pool. */ 2587 if (entry->name) 2588 dupentry->name = apr_pstrdup(pool, entry->name); 2589 if (entry->url) 2590 dupentry->url = apr_pstrdup(pool, entry->url); 2591 if (entry->repos) 2592 dupentry->repos = apr_pstrdup(pool, entry->repos); 2593 if (entry->uuid) 2594 dupentry->uuid = apr_pstrdup(pool, entry->uuid); 2595 if (entry->copyfrom_url) 2596 dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url); 2597 if (entry->conflict_old) 2598 dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old); 2599 if (entry->conflict_new) 2600 dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new); 2601 if (entry->conflict_wrk) 2602 dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk); 2603 if (entry->prejfile) 2604 dupentry->prejfile = apr_pstrdup(pool, entry->prejfile); 2605 if (entry->checksum) 2606 dupentry->checksum = apr_pstrdup(pool, entry->checksum); 2607 if (entry->cmt_author) 2608 dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author); 2609 if (entry->lock_token) 2610 dupentry->lock_token = apr_pstrdup(pool, entry->lock_token); 2611 if (entry->lock_owner) 2612 dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner); 2613 if (entry->lock_comment) 2614 dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment); 2615 if (entry->changelist) 2616 dupentry->changelist = apr_pstrdup(pool, entry->changelist); 2617 2618 /* NOTE: we do not dup cachable_props or present_props since they 2619 are deprecated. Use "" to indicate "nothing cachable or cached". */ 2620 dupentry->cachable_props = ""; 2621 dupentry->present_props = ""; 2622 2623 if (entry->tree_conflict_data) 2624 dupentry->tree_conflict_data = apr_pstrdup(pool, 2625 entry->tree_conflict_data); 2626 if (entry->file_external_path) 2627 dupentry->file_external_path = apr_pstrdup(pool, 2628 entry->file_external_path); 2629 return dupentry; 2630} 2631 2632 2633/*** Generic Entry Walker */ 2634 2635/* A recursive entry-walker, helper for svn_wc_walk_entries3(). 2636 * 2637 * For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback 2638 * in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned 2639 * entry in this directory, call the "found entry" callback and then recurse 2640 * (if it is a directory and if DEPTH allows). 2641 * 2642 * If SHOW_HIDDEN is true, include entries that are in a 'deleted' or 2643 * 'absent' state (and not scheduled for re-addition), else skip them. 2644 * 2645 * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation. 2646 */ 2647static svn_error_t * 2648walker_helper(const char *dirpath, 2649 svn_wc_adm_access_t *adm_access, 2650 const svn_wc_entry_callbacks2_t *walk_callbacks, 2651 void *walk_baton, 2652 svn_depth_t depth, 2653 svn_boolean_t show_hidden, 2654 svn_cancel_func_t cancel_func, 2655 void *cancel_baton, 2656 apr_pool_t *pool) 2657{ 2658 apr_pool_t *subpool = svn_pool_create(pool); 2659 apr_hash_t *entries; 2660 apr_hash_index_t *hi; 2661 svn_wc_entry_t *dot_entry; 2662 svn_error_t *err; 2663 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 2664 2665 err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden, 2666 pool); 2667 2668 if (err) 2669 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool)); 2670 2671 /* As promised, always return the '.' entry first. */ 2672 dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 2673 if (! dot_entry) 2674 return walk_callbacks->handle_error 2675 (dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 2676 _("Directory '%s' has no THIS_DIR entry"), 2677 svn_dirent_local_style(dirpath, pool)), 2678 walk_baton, pool); 2679 2680 /* Call the "found entry" callback for this directory as a "this dir" 2681 * entry. Note that if this directory has been reached by recursion, this 2682 * is the second visit as it will already have been visited once as a 2683 * child entry of its parent. */ 2684 2685 err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool); 2686 2687 2688 if(err) 2689 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool)); 2690 2691 if (depth == svn_depth_empty) 2692 return SVN_NO_ERROR; 2693 2694 /* Loop over each of the other entries. */ 2695 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 2696 { 2697 const char *name = apr_hash_this_key(hi); 2698 const svn_wc_entry_t *current_entry = apr_hash_this_val(hi); 2699 const char *entrypath; 2700 const char *entry_abspath; 2701 svn_boolean_t hidden; 2702 2703 svn_pool_clear(subpool); 2704 2705 /* See if someone wants to cancel this operation. */ 2706 if (cancel_func) 2707 SVN_ERR(cancel_func(cancel_baton)); 2708 2709 /* Skip the "this dir" entry. */ 2710 if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0) 2711 continue; 2712 2713 entrypath = svn_dirent_join(dirpath, name, subpool); 2714 SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry)); 2715 SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool)); 2716 2717 /* Call the "found entry" callback for this entry. (For a directory, 2718 * this is the first visit: as a child.) */ 2719 if (current_entry->kind == svn_node_file 2720 || depth >= svn_depth_immediates) 2721 { 2722 err = walk_callbacks->found_entry(entrypath, current_entry, 2723 walk_baton, subpool); 2724 2725 if (err) 2726 SVN_ERR(walk_callbacks->handle_error(entrypath, err, 2727 walk_baton, pool)); 2728 } 2729 2730 /* Recurse into this entry if appropriate. */ 2731 if (current_entry->kind == svn_node_dir 2732 && !hidden 2733 && depth >= svn_depth_immediates) 2734 { 2735 svn_wc_adm_access_t *entry_access; 2736 svn_depth_t depth_below_here = depth; 2737 2738 if (depth == svn_depth_immediates) 2739 depth_below_here = svn_depth_empty; 2740 2741 entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath, 2742 subpool); 2743 2744 if (entry_access) 2745 SVN_ERR(walker_helper(entrypath, entry_access, 2746 walk_callbacks, walk_baton, 2747 depth_below_here, show_hidden, 2748 cancel_func, cancel_baton, 2749 subpool)); 2750 } 2751 } 2752 2753 svn_pool_destroy(subpool); 2754 return SVN_NO_ERROR; 2755} 2756 2757svn_error_t * 2758svn_wc__walker_default_error_handler(const char *path, 2759 svn_error_t *err, 2760 void *walk_baton, 2761 apr_pool_t *pool) 2762{ 2763 /* Note: don't trace this. We don't want to insert a false "stack frame" 2764 onto an error generated elsewhere. */ 2765 return svn_error_trace(err); 2766} 2767 2768 2769/* The public API. */ 2770svn_error_t * 2771svn_wc_walk_entries3(const char *path, 2772 svn_wc_adm_access_t *adm_access, 2773 const svn_wc_entry_callbacks2_t *walk_callbacks, 2774 void *walk_baton, 2775 svn_depth_t walk_depth, 2776 svn_boolean_t show_hidden, 2777 svn_cancel_func_t cancel_func, 2778 void *cancel_baton, 2779 apr_pool_t *pool) 2780{ 2781 const char *local_abspath; 2782 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 2783 svn_error_t *err; 2784 svn_node_kind_t kind; 2785 svn_wc__db_status_t status; 2786 2787 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 2788 err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, 2789 NULL, NULL, NULL, NULL, NULL, NULL, 2790 NULL, NULL, NULL, NULL, NULL, NULL, 2791 NULL, NULL, NULL, NULL, NULL, NULL, 2792 NULL, NULL, NULL, 2793 db, local_abspath, 2794 pool, pool); 2795 if (err) 2796 { 2797 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2798 return svn_error_trace(err); 2799 /* Remap into SVN_ERR_UNVERSIONED_RESOURCE. */ 2800 svn_error_clear(err); 2801 return walk_callbacks->handle_error( 2802 path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL, 2803 _("'%s' is not under version control"), 2804 svn_dirent_local_style(local_abspath, pool)), 2805 walk_baton, pool); 2806 } 2807 2808 if (kind == svn_node_file 2809 || status == svn_wc__db_status_excluded 2810 || status == svn_wc__db_status_server_excluded) 2811 { 2812 const svn_wc_entry_t *entry; 2813 2814 /* ### we should stop passing out entry structures. 2815 ### 2816 ### we should not call handle_error for an error the *callback* 2817 ### gave us. let it deal with the problem before returning. */ 2818 2819 if (!show_hidden 2820 && (status == svn_wc__db_status_not_present 2821 || status == svn_wc__db_status_excluded 2822 || status == svn_wc__db_status_server_excluded)) 2823 { 2824 /* The fool asked to walk a "hidden" node. Report the node as 2825 unversioned. 2826 2827 ### this is incorrect behavior. see depth_test 36. the walk 2828 ### API will be revamped to avoid entry structures. we should 2829 ### be able to solve the problem with the new API. (since we 2830 ### shouldn't return a hidden entry here) */ 2831 return walk_callbacks->handle_error( 2832 path, svn_error_createf( 2833 SVN_ERR_UNVERSIONED_RESOURCE, NULL, 2834 _("'%s' is not under version control"), 2835 svn_dirent_local_style(local_abspath, pool)), 2836 walk_baton, pool); 2837 } 2838 2839 SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE, 2840 svn_node_file, pool, pool)); 2841 2842 err = walk_callbacks->found_entry(path, entry, walk_baton, pool); 2843 if (err) 2844 return walk_callbacks->handle_error(path, err, walk_baton, pool); 2845 2846 return SVN_NO_ERROR; 2847 } 2848 2849 if (kind == svn_node_dir) 2850 return walker_helper(path, adm_access, walk_callbacks, walk_baton, 2851 walk_depth, show_hidden, cancel_func, cancel_baton, 2852 pool); 2853 2854 return walk_callbacks->handle_error( 2855 path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL, 2856 _("'%s' has an unrecognized node kind"), 2857 svn_dirent_local_style(local_abspath, pool)), 2858 walk_baton, pool); 2859} 2860