1/* 2 * update_editor.c : main editor for checkouts and updates 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26#include <stdlib.h> 27#include <string.h> 28 29#include <apr_pools.h> 30#include <apr_hash.h> 31#include <apr_md5.h> 32#include <apr_tables.h> 33#include <apr_strings.h> 34 35#include "svn_types.h" 36#include "svn_pools.h" 37#include "svn_hash.h" 38#include "svn_string.h" 39#include "svn_dirent_uri.h" 40#include "svn_path.h" 41#include "svn_error.h" 42#include "svn_io.h" 43#include "svn_private_config.h" 44#include "svn_time.h" 45 46#include "wc.h" 47#include "adm_files.h" 48#include "conflicts.h" 49#include "translate.h" 50#include "workqueue.h" 51 52#include "private/svn_subr_private.h" 53#include "private/svn_wc_private.h" 54#include "private/svn_editor.h" 55 56/* Checks whether a svn_wc__db_status_t indicates whether a node is 57 present in a working copy. Used by the editor implementation */ 58#define IS_NODE_PRESENT(status) \ 59 ((status) != svn_wc__db_status_server_excluded &&\ 60 (status) != svn_wc__db_status_excluded && \ 61 (status) != svn_wc__db_status_not_present) 62 63static svn_error_t * 64path_join_under_root(const char **result_path, 65 const char *base_path, 66 const char *add_path, 67 apr_pool_t *result_pool); 68 69 70/* 71 * This code handles "checkout" and "update" and "switch". 72 * A checkout is similar to an update that is only adding new items. 73 * 74 * The intended behaviour of "update" and "switch", focusing on the checks 75 * to be made before applying a change, is: 76 * 77 * For each incoming change: 78 * if target is already in conflict or obstructed: 79 * skip this change 80 * else 81 * if this action will cause a tree conflict: 82 * record the tree conflict 83 * skip this change 84 * else: 85 * make this change 86 * 87 * In more detail: 88 * 89 * For each incoming change: 90 * 91 * 1. if # Incoming change is inside an item already in conflict: 92 * a. tree/text/prop change to node beneath tree-conflicted dir 93 * then # Skip all changes in this conflicted subtree [*1]: 94 * do not update the Base nor the Working 95 * notify "skipped because already in conflict" just once 96 * for the whole conflicted subtree 97 * 98 * if # Incoming change affects an item already in conflict: 99 * b. tree/text/prop change to tree-conflicted dir/file, or 100 * c. tree change to a text/prop-conflicted file/dir, or 101 * d. text/prop change to a text/prop-conflicted file/dir [*2], or 102 * e. tree change to a dir tree containing any conflicts, 103 * then # Skip this change [*1]: 104 * do not update the Base nor the Working 105 * notify "skipped because already in conflict" 106 * 107 * 2. if # Incoming change affects an item that's "obstructed": 108 * a. on-disk node kind doesn't match recorded Working node kind 109 * (including an absence/presence mis-match), 110 * then # Skip this change [*1]: 111 * do not update the Base nor the Working 112 * notify "skipped because obstructed" 113 * 114 * 3. if # Incoming change raises a tree conflict: 115 * a. tree/text/prop change to node beneath sched-delete dir, or 116 * b. tree/text/prop change to sched-delete dir/file, or 117 * c. text/prop change to tree-scheduled dir/file, 118 * then # Skip this change: 119 * do not update the Base nor the Working [*3] 120 * notify "tree conflict" 121 * 122 * 4. Apply the change: 123 * update the Base 124 * update the Working, possibly raising text/prop conflicts 125 * notify 126 * 127 * Notes: 128 * 129 * "Tree change" here refers to an add or delete of the target node, 130 * including the add or delete part of a copy or move or rename. 131 * 132 * [*1] We should skip changes to an entire node, as the base revision number 133 * applies to the entire node. Not sure how this affects attempts to 134 * handle text and prop changes separately. 135 * 136 * [*2] Details of which combinations of property and text changes conflict 137 * are not specified here. 138 * 139 * [*3] For now, we skip the update, and require the user to: 140 * - Modify the WC to be compatible with the incoming change; 141 * - Mark the conflict as resolved; 142 * - Repeat the update. 143 * Ideally, it would be possible to resolve any conflict without 144 * repeating the update. To achieve this, we would have to store the 145 * necessary data at conflict detection time, and delay the update of 146 * the Base until the time of resolving. 147 */ 148 149 150/*** batons ***/ 151 152struct edit_baton 153{ 154 /* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the 155 directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the 156 target, the values are identical. 157 158 TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if 159 ANCHOR_ABSPATH is the target */ 160 const char *target_basename; 161 162 /* Absolute variants of ANCHOR and TARGET */ 163 const char *anchor_abspath; 164 const char *target_abspath; 165 166 /* The DB handle for managing the working copy state. */ 167 svn_wc__db_t *db; 168 169 /* Array of file extension patterns to preserve as extensions in 170 generated conflict files. */ 171 const apr_array_header_t *ext_patterns; 172 173 /* Hash mapping const char * absolute working copy paths to depth-first 174 ordered arrays of svn_prop_inherited_item_t * structures representing 175 the properties inherited by the base node at that working copy path. 176 May be NULL. */ 177 apr_hash_t *wcroot_iprops; 178 179 /* The revision we're targeting...or something like that. This 180 starts off as a pointer to the revision to which we are updating, 181 or SVN_INVALID_REVNUM, but by the end of the edit, should be 182 pointing to the final revision. */ 183 svn_revnum_t *target_revision; 184 185 /* The requested depth of this edit. */ 186 svn_depth_t requested_depth; 187 188 /* Is the requested depth merely an operational limitation, or is 189 also the new sticky ambient depth of the update target? */ 190 svn_boolean_t depth_is_sticky; 191 192 /* Need to know if the user wants us to overwrite the 'now' times on 193 edited/added files with the last-commit-time. */ 194 svn_boolean_t use_commit_times; 195 196 /* Was the root actually opened (was this a non-empty edit)? */ 197 svn_boolean_t root_opened; 198 199 /* Was the update-target deleted? This is a special situation. */ 200 svn_boolean_t target_deleted; 201 202 /* Allow unversioned obstructions when adding a path. */ 203 svn_boolean_t allow_unver_obstructions; 204 205 /* Handle local additions as modifications of new nodes */ 206 svn_boolean_t adds_as_modification; 207 208 /* If set, we check out into an empty directory. This allows for a number 209 of conflict checks to be omitted. */ 210 svn_boolean_t clean_checkout; 211 212 /* If this is a 'switch' operation, the new relpath of target_abspath, 213 else NULL. */ 214 const char *switch_relpath; 215 216 /* The URL to the root of the repository. */ 217 const char *repos_root; 218 219 /* The UUID of the repos, or NULL. */ 220 const char *repos_uuid; 221 222 /* External diff3 to use for merges (can be null, in which case 223 internal merge code is used). */ 224 const char *diff3_cmd; 225 226 /* Externals handler */ 227 svn_wc_external_update_t external_func; 228 void *external_baton; 229 230 /* This editor sends back notifications as it edits. */ 231 svn_wc_notify_func2_t notify_func; 232 void *notify_baton; 233 234 /* This editor is normally wrapped in a cancellation editor anyway, 235 so it doesn't bother to check for cancellation itself. However, 236 it needs a cancel_func and cancel_baton available to pass to 237 long-running functions. */ 238 svn_cancel_func_t cancel_func; 239 void *cancel_baton; 240 241 /* This editor will invoke a interactive conflict-resolution 242 callback, if available. */ 243 svn_wc_conflict_resolver_func2_t conflict_func; 244 void *conflict_baton; 245 246 /* Subtrees that were skipped during the edit, and therefore shouldn't 247 have their revision/url info updated at the end. If a path is a 248 directory, its descendants will also be skipped. The keys are paths 249 relative to the working copy root and the values unspecified. */ 250 apr_hash_t *skipped_trees; 251 252 /* A mapping from const char * repos_relpaths to the apr_hash_t * instances 253 returned from fetch_dirents_func for that repos_relpath. These 254 are used to avoid issue #3569 in specific update scenarios where a 255 restricted depth is used. */ 256 apr_hash_t *dir_dirents; 257 258 /* Absolute path of the working copy root or NULL if not initialized yet */ 259 const char *wcroot_abspath; 260 261 apr_pool_t *pool; 262}; 263 264 265/* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being 266 * updated. 267 * 268 * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string 269 * LOCAL_ABSPATH. 270 */ 271static svn_error_t * 272remember_skipped_tree(struct edit_baton *eb, 273 const char *local_abspath, 274 apr_pool_t *scratch_pool) 275{ 276 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 277 278 svn_hash_sets(eb->skipped_trees, 279 apr_pstrdup(eb->pool, 280 svn_dirent_skip_ancestor(eb->wcroot_abspath, 281 local_abspath)), 282 (void *)1); 283 284 return SVN_NO_ERROR; 285} 286 287/* Per directory baton. Lives in its own subpool of the parent directory 288 or of the edit baton if there is no parent directory */ 289struct dir_baton 290{ 291 /* Basename of this directory. */ 292 const char *name; 293 294 /* Absolute path of this directory */ 295 const char *local_abspath; 296 297 /* The repository relative path this directory will correspond to. */ 298 const char *new_relpath; 299 300 /* The revision of the directory before updating */ 301 svn_revnum_t old_revision; 302 303 /* The repos_relpath before updating/switching */ 304 const char *old_repos_relpath; 305 306 /* The global edit baton. */ 307 struct edit_baton *edit_baton; 308 309 /* Baton for this directory's parent, or NULL if this is the root 310 directory. */ 311 struct dir_baton *parent_baton; 312 313 /* Set if updates to this directory are skipped */ 314 svn_boolean_t skip_this; 315 316 /* Set if there was a previous notification for this directory */ 317 svn_boolean_t already_notified; 318 319 /* Set if this directory is being added during this editor drive. */ 320 svn_boolean_t adding_dir; 321 322 /* Set on a node and its descendants are not present in the working copy 323 but should still be updated (not skipped). These nodes should all be 324 marked as deleted. */ 325 svn_boolean_t shadowed; 326 327 /* Set on a node when the existing node is obstructed, and the edit operation 328 continues as semi-shadowed update */ 329 svn_boolean_t edit_obstructed; 330 331 /* The (new) changed_* information, cached to avoid retrieving it later */ 332 svn_revnum_t changed_rev; 333 apr_time_t changed_date; 334 const char *changed_author; 335 336 /* If not NULL, contains a mapping of const char* basenames of children that 337 have been deleted to their svn_skel_t* tree conflicts. 338 We store this hash to allow replacements to continue under a just 339 installed tree conflict. 340 341 The add after the delete will then update the tree conflicts information 342 and reinstall it. */ 343 apr_hash_t *deletion_conflicts; 344 345 /* A hash of file names (only the hash key matters) seen by add_file 346 and not yet added to the database by close_file. */ 347 apr_hash_t *not_present_files; 348 349 /* Set if an unversioned dir of the same name already existed in 350 this directory. */ 351 svn_boolean_t obstruction_found; 352 353 /* Set if a dir of the same name already exists and is 354 scheduled for addition without history. */ 355 svn_boolean_t add_existed; 356 357 /* An array of svn_prop_t structures, representing all the property 358 changes to be applied to this directory. */ 359 apr_array_header_t *propchanges; 360 361 /* A boolean indicating whether this node or one of its children has 362 received any 'real' changes. Used to avoid tree conflicts for simple 363 entryprop changes, like lock management */ 364 svn_boolean_t edited; 365 366 /* The tree conflict to install once the node is really edited */ 367 svn_skel_t *edit_conflict; 368 369 /* The bump information for this directory. */ 370 struct bump_dir_info *bump_info; 371 372 /* The depth of the directory in the wc (or inferred if added). Not 373 used for filtering; we have a separate wrapping editor for that. */ 374 svn_depth_t ambient_depth; 375 376 /* Was the directory marked as incomplete before the update? 377 (In other words, are we resuming an interrupted update?) 378 379 If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes 380 and properties for/of the directory. If WAS_INCOMPLETE is FALSE then 381 we only receive the changes in/for children and properties.*/ 382 svn_boolean_t was_incomplete; 383 384 /* The pool in which this baton itself is allocated. */ 385 apr_pool_t *pool; 386 387 /* how many nodes are referring to baton? */ 388 int ref_count; 389 390}; 391 392 393struct handler_baton 394{ 395 svn_txdelta_window_handler_t apply_handler; 396 void *apply_baton; 397 apr_pool_t *pool; 398 struct file_baton *fb; 399 400 /* Where we are assembling the new file. */ 401 const char *new_text_base_tmp_abspath; 402 403 /* The expected source checksum of the text source or NULL if no base 404 checksum is available (MD5 if the server provides a checksum, SHA1 if 405 the server doesn't) */ 406 svn_checksum_t *expected_source_checksum; 407 408 /* Why two checksums? 409 The editor currently provides an md5 which we use to detect corruption 410 during transmission. We use the sha1 inside libsvn_wc both for pristine 411 handling and corruption detection. In the future, the editor will also 412 provide a sha1, so we may not have to calculate both, but for the time 413 being, that's the way it is. */ 414 415 /* The calculated checksum of the text source or NULL if the acual 416 checksum is not being calculated. The checksum kind is identical to the 417 kind of expected_source_checksum. */ 418 svn_checksum_t *actual_source_checksum; 419 420 /* The stream used to calculate the source checksums */ 421 svn_stream_t *source_checksum_stream; 422 423 /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH. 424 This is initialized to all zeroes when the baton is created, then 425 populated with the MD5 digest of the resultant fulltext after the 426 last window is handled by the handler returned from 427 apply_textdelta(). */ 428 unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE]; 429 430 /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for 431 eventually writing the pristine. */ 432 svn_checksum_t * new_text_base_sha1_checksum; 433}; 434 435 436/* Get an empty file in the temporary area for WRI_ABSPATH. The file will 437 not be set for automatic deletion, and the name will be returned in 438 TMP_FILENAME. 439 440 This implementation creates a new empty file with a unique name. 441 442 ### This is inefficient for callers that just want an empty file to read 443 ### from. There could be (and there used to be) a permanent, shared 444 ### empty file for this purpose. 445 446 ### This is inefficient for callers that just want to reserve a unique 447 ### file name to create later. A better way may not be readily available. 448 */ 449static svn_error_t * 450get_empty_tmp_file(const char **tmp_filename, 451 svn_wc__db_t *db, 452 const char *wri_abspath, 453 apr_pool_t *result_pool, 454 apr_pool_t *scratch_pool) 455{ 456 const char *temp_dir_abspath; 457 458 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath, 459 scratch_pool, scratch_pool)); 460 SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath, 461 svn_io_file_del_none, 462 scratch_pool, scratch_pool)); 463 464 return SVN_NO_ERROR; 465} 466 467/* An APR pool cleanup handler. This runs the working queue for an 468 editor baton. */ 469static apr_status_t 470cleanup_edit_baton(void *edit_baton) 471{ 472 struct edit_baton *eb = edit_baton; 473 svn_error_t *err; 474 apr_pool_t *pool = apr_pool_parent_get(eb->pool); 475 476 err = svn_wc__wq_run(eb->db, eb->wcroot_abspath, 477 NULL /* cancel_func */, NULL /* cancel_baton */, 478 pool); 479 480 if (err) 481 { 482 apr_status_t apr_err = err->apr_err; 483 svn_error_clear(err); 484 return apr_err; 485 } 486 return APR_SUCCESS; 487} 488 489/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton. 490 If PATH and PB are NULL, this is the root directory of the edit; in this 491 case, make the new dir baton in a subpool of EB->pool. 492 ADDING should be TRUE if we are adding this directory. */ 493static svn_error_t * 494make_dir_baton(struct dir_baton **d_p, 495 const char *path, 496 struct edit_baton *eb, 497 struct dir_baton *pb, 498 svn_boolean_t adding, 499 apr_pool_t *scratch_pool) 500{ 501 apr_pool_t *dir_pool; 502 struct dir_baton *d; 503 504 if (pb != NULL) 505 dir_pool = svn_pool_create(pb->pool); 506 else 507 dir_pool = svn_pool_create(eb->pool); 508 509 SVN_ERR_ASSERT(path || (! pb)); 510 511 /* Okay, no easy out, so allocate and initialize a dir baton. */ 512 d = apr_pcalloc(dir_pool, sizeof(*d)); 513 514 /* Construct the PATH and baseNAME of this directory. */ 515 if (path) 516 { 517 d->name = svn_dirent_basename(path, dir_pool); 518 SVN_ERR(path_join_under_root(&d->local_abspath, 519 pb->local_abspath, d->name, dir_pool)); 520 } 521 else 522 { 523 /* This is the root baton. */ 524 d->name = NULL; 525 d->local_abspath = eb->anchor_abspath; 526 } 527 528 /* Figure out the new_relpath for this directory. */ 529 if (eb->switch_relpath) 530 { 531 /* Handle switches... */ 532 533 if (pb == NULL) 534 { 535 if (*eb->target_basename == '\0') 536 { 537 /* No parent baton and target_basename=="" means that we are 538 the target of the switch. Thus, our NEW_RELPATH will be 539 the SWITCH_RELPATH. */ 540 d->new_relpath = eb->switch_relpath; 541 } 542 else 543 { 544 /* This node is NOT the target of the switch (one of our 545 children is the target); therefore, it must already exist. 546 Get its old REPOS_RELPATH, as it won't be changing. */ 547 SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL, 548 eb->db, d->local_abspath, 549 dir_pool, scratch_pool)); 550 } 551 } 552 else 553 { 554 /* This directory is *not* the root (has a parent). If there is 555 no grandparent, then we may have anchored at the parent, 556 and self is the target. If we match the target, then set 557 NEW_RELPATH to the SWITCH_RELPATH. 558 559 Otherwise, we simply extend NEW_RELPATH from the parent. */ 560 if (pb->parent_baton == NULL 561 && strcmp(eb->target_basename, d->name) == 0) 562 d->new_relpath = eb->switch_relpath; 563 else 564 d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, 565 dir_pool); 566 } 567 } 568 else /* must be an update */ 569 { 570 /* If we are adding the node, then simply extend the parent's 571 relpath for our own. */ 572 if (adding) 573 { 574 SVN_ERR_ASSERT(pb != NULL); 575 d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, 576 dir_pool); 577 } 578 else 579 { 580 SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL, 581 eb->db, d->local_abspath, 582 dir_pool, scratch_pool)); 583 SVN_ERR_ASSERT(d->new_relpath); 584 } 585 } 586 587 d->edit_baton = eb; 588 d->parent_baton = pb; 589 d->pool = dir_pool; 590 d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t)); 591 d->obstruction_found = FALSE; 592 d->add_existed = FALSE; 593 d->ref_count = 1; 594 d->old_revision = SVN_INVALID_REVNUM; 595 d->adding_dir = adding; 596 d->changed_rev = SVN_INVALID_REVNUM; 597 d->not_present_files = apr_hash_make(dir_pool); 598 599 /* Copy some flags from the parent baton */ 600 if (pb) 601 { 602 d->skip_this = pb->skip_this; 603 d->shadowed = pb->shadowed || pb->edit_obstructed; 604 605 /* the parent's bump info has one more referer */ 606 pb->ref_count++; 607 } 608 609 /* The caller of this function needs to fill these in. */ 610 d->ambient_depth = svn_depth_unknown; 611 d->was_incomplete = FALSE; 612 613 *d_p = d; 614 return SVN_NO_ERROR; 615} 616 617 618/* Forward declarations. */ 619static svn_error_t * 620already_in_a_tree_conflict(svn_boolean_t *conflicted, 621 svn_boolean_t *ignored, 622 svn_wc__db_t *db, 623 const char *local_abspath, 624 apr_pool_t *scratch_pool); 625 626 627static void 628do_notification(const struct edit_baton *eb, 629 const char *local_abspath, 630 svn_node_kind_t kind, 631 svn_wc_notify_action_t action, 632 apr_pool_t *scratch_pool) 633{ 634 svn_wc_notify_t *notify; 635 636 if (eb->notify_func == NULL) 637 return; 638 639 notify = svn_wc_create_notify(local_abspath, action, scratch_pool); 640 notify->kind = kind; 641 642 (*eb->notify_func)(eb->notify_baton, notify, scratch_pool); 643} 644 645/* Decrement the directory's reference count. If it hits zero, 646 then this directory is "done". This means it is safe to clear its pool. 647 648 In addition, when the directory is "done", we recurse to possible cleanup 649 the parent directory. 650*/ 651static svn_error_t * 652maybe_release_dir_info(struct dir_baton *db) 653{ 654 db->ref_count--; 655 656 if (!db->ref_count) 657 { 658 struct dir_baton *pb = db->parent_baton; 659 660 svn_pool_destroy(db->pool); 661 662 if (pb) 663 SVN_ERR(maybe_release_dir_info(pb)); 664 } 665 666 return SVN_NO_ERROR; 667} 668 669/* Per file baton. Lives in its own subpool below the pool of the parent 670 directory */ 671struct file_baton 672{ 673 /* Pool specific to this file_baton. */ 674 apr_pool_t *pool; 675 676 /* Name of this file (its entry in the directory). */ 677 const char *name; 678 679 /* Absolute path to this file */ 680 const char *local_abspath; 681 682 /* The repository relative path this file will correspond to. */ 683 const char *new_relpath; 684 685 /* The revision of the file before updating */ 686 svn_revnum_t old_revision; 687 688 /* The repos_relpath before updating/switching */ 689 const char *old_repos_relpath; 690 691 /* The global edit baton. */ 692 struct edit_baton *edit_baton; 693 694 /* The parent directory of this file. */ 695 struct dir_baton *dir_baton; 696 697 /* Set if updates to this directory are skipped */ 698 svn_boolean_t skip_this; 699 700 /* Set if there was a previous notification */ 701 svn_boolean_t already_notified; 702 703 /* Set if this file is new. */ 704 svn_boolean_t adding_file; 705 706 /* Set if an unversioned file of the same name already existed in 707 this directory. */ 708 svn_boolean_t obstruction_found; 709 710 /* Set if a file of the same name already exists and is 711 scheduled for addition without history. */ 712 svn_boolean_t add_existed; 713 714 /* Set if this file is being added in the BASE layer, but is not-present 715 in the working copy (replaced, deleted, etc.). */ 716 svn_boolean_t shadowed; 717 718 /* Set on a node when the existing node is obstructed, and the edit operation 719 continues as semi-shadowed update */ 720 svn_boolean_t edit_obstructed; 721 722 /* The (new) changed_* information, cached to avoid retrieving it later */ 723 svn_revnum_t changed_rev; 724 apr_time_t changed_date; 725 const char *changed_author; 726 727 /* If there are file content changes, these are the checksums of the 728 resulting new text base, which is in the pristine store, else NULL. */ 729 const svn_checksum_t *new_text_base_md5_checksum; 730 const svn_checksum_t *new_text_base_sha1_checksum; 731 732 /* The checksum of the file before the update */ 733 const svn_checksum_t *original_checksum; 734 735 /* An array of svn_prop_t structures, representing all the property 736 changes to be applied to this file. Once a file baton is 737 initialized, this is never NULL, but it may have zero elements. */ 738 apr_array_header_t *propchanges; 739 740 /* For existing files, whether there are local modifications. FALSE for added 741 files */ 742 svn_boolean_t local_prop_mods; 743 744 /* Bump information for the directory this file lives in */ 745 struct bump_dir_info *bump_info; 746 747 /* A boolean indicating whether this node or one of its children has 748 received any 'real' changes. Used to avoid tree conflicts for simple 749 entryprop changes, like lock management */ 750 svn_boolean_t edited; 751 752 /* The tree conflict to install once the node is really edited */ 753 svn_skel_t *edit_conflict; 754}; 755 756 757/* Make a new file baton in a subpool of PB->pool. PB is the parent baton. 758 * PATH is relative to the root of the edit. ADDING tells whether this file 759 * is being added. */ 760static svn_error_t * 761make_file_baton(struct file_baton **f_p, 762 struct dir_baton *pb, 763 const char *path, 764 svn_boolean_t adding, 765 apr_pool_t *scratch_pool) 766{ 767 struct edit_baton *eb = pb->edit_baton; 768 apr_pool_t *file_pool = svn_pool_create(pb->pool); 769 struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f)); 770 771 SVN_ERR_ASSERT(path); 772 773 /* Make the file's on-disk name. */ 774 f->name = svn_dirent_basename(path, file_pool); 775 f->old_revision = SVN_INVALID_REVNUM; 776 SVN_ERR(path_join_under_root(&f->local_abspath, 777 pb->local_abspath, f->name, file_pool)); 778 779 /* Figure out the new URL for this file. */ 780 if (eb->switch_relpath) 781 { 782 /* Handle switches... */ 783 784 /* This file has a parent directory. If there is 785 no grandparent, then we may have anchored at the parent, 786 and self is the target. If we match the target, then set 787 NEW_RELPATH to the SWITCH_RELPATH. 788 789 Otherwise, we simply extend NEW_RELPATH from the parent. */ 790 if (pb->parent_baton == NULL 791 && strcmp(eb->target_basename, f->name) == 0) 792 f->new_relpath = eb->switch_relpath; 793 else 794 f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, 795 file_pool); 796 } 797 else /* must be an update */ 798 { 799 if (adding) 800 f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool); 801 else 802 { 803 SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL, 804 eb->db, f->local_abspath, 805 file_pool, scratch_pool)); 806 SVN_ERR_ASSERT(f->new_relpath); 807 } 808 } 809 810 f->pool = file_pool; 811 f->edit_baton = pb->edit_baton; 812 f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t)); 813 f->bump_info = pb->bump_info; 814 f->adding_file = adding; 815 f->obstruction_found = FALSE; 816 f->add_existed = FALSE; 817 f->skip_this = pb->skip_this; 818 f->shadowed = pb->shadowed || pb->edit_obstructed; 819 f->dir_baton = pb; 820 f->changed_rev = SVN_INVALID_REVNUM; 821 822 /* the directory has one more referer now */ 823 pb->ref_count++; 824 825 *f_p = f; 826 return SVN_NO_ERROR; 827} 828 829/* Complete a conflict skel by describing the update. 830 * 831 * LOCAL_KIND is the node kind of the tree conflict victim in the 832 * working copy. 833 * 834 * All temporary allocations are be made in SCRATCH_POOL, while allocations 835 * needed for the returned conflict struct are made in RESULT_POOL. 836 */ 837static svn_error_t * 838complete_conflict(svn_skel_t *conflict, 839 const struct edit_baton *eb, 840 const char *local_abspath, 841 const char *old_repos_relpath, 842 svn_revnum_t old_revision, 843 const char *new_repos_relpath, 844 svn_node_kind_t local_kind, 845 svn_node_kind_t target_kind, 846 apr_pool_t *result_pool, 847 apr_pool_t *scratch_pool) 848{ 849 svn_wc_conflict_version_t *original_version; 850 svn_wc_conflict_version_t *target_version; 851 svn_boolean_t is_complete; 852 853 if (!conflict) 854 return SVN_NO_ERROR; /* Not conflicted */ 855 856 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict)); 857 858 if (is_complete) 859 return SVN_NO_ERROR; /* Already completed */ 860 861 if (old_repos_relpath) 862 original_version = svn_wc_conflict_version_create2(eb->repos_root, 863 eb->repos_uuid, 864 old_repos_relpath, 865 old_revision, 866 local_kind, 867 result_pool); 868 else 869 original_version = NULL; 870 871 if (new_repos_relpath) 872 target_version = svn_wc_conflict_version_create2(eb->repos_root, 873 eb->repos_uuid, 874 new_repos_relpath, 875 *eb->target_revision, 876 target_kind, 877 result_pool); 878 else 879 target_version = NULL; 880 881 if (eb->switch_relpath) 882 SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict, 883 original_version, 884 target_version, 885 result_pool, scratch_pool)); 886 else 887 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict, 888 original_version, 889 target_version, 890 result_pool, scratch_pool)); 891 892 return SVN_NO_ERROR; 893} 894 895 896/* Called when a directory is really edited, to avoid marking a 897 tree conflict on a node for a no-change edit */ 898static svn_error_t * 899mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool) 900{ 901 if (db->edited) 902 return SVN_NO_ERROR; 903 904 if (db->parent_baton) 905 SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool)); 906 907 db->edited = TRUE; 908 909 if (db->edit_conflict) 910 { 911 /* We have a (delayed) tree conflict to install */ 912 913 SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton, 914 db->local_abspath, 915 db->old_repos_relpath, db->old_revision, 916 db->new_relpath, 917 svn_node_dir, svn_node_dir, 918 db->pool, scratch_pool)); 919 SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db, 920 db->local_abspath, 921 db->edit_conflict, NULL, 922 scratch_pool)); 923 924 do_notification(db->edit_baton, db->local_abspath, svn_node_dir, 925 svn_wc_notify_tree_conflict, scratch_pool); 926 db->already_notified = TRUE; 927 928 } 929 930 return SVN_NO_ERROR; 931} 932 933/* Called when a file is really edited, to avoid marking a 934 tree conflict on a node for a no-change edit */ 935static svn_error_t * 936mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool) 937{ 938 if (fb->edited) 939 return SVN_NO_ERROR; 940 941 SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool)); 942 943 fb->edited = TRUE; 944 945 if (fb->edit_conflict) 946 { 947 /* We have a (delayed) tree conflict to install */ 948 949 SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, 950 fb->local_abspath, fb->old_repos_relpath, 951 fb->old_revision, fb->new_relpath, 952 svn_node_file, svn_node_file, 953 fb->pool, scratch_pool)); 954 955 SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db, 956 fb->local_abspath, 957 fb->edit_conflict, NULL, 958 scratch_pool)); 959 960 do_notification(fb->edit_baton, fb->local_abspath, svn_node_file, 961 svn_wc_notify_tree_conflict, scratch_pool); 962 fb->already_notified = TRUE; 963 } 964 965 return SVN_NO_ERROR; 966} 967 968 969/* Handle the next delta window of the file described by BATON. If it is 970 * the end (WINDOW == NULL), then check the checksum, store the text in the 971 * pristine store and write its details into BATON->fb->new_text_base_*. */ 972static svn_error_t * 973window_handler(svn_txdelta_window_t *window, void *baton) 974{ 975 struct handler_baton *hb = baton; 976 struct file_baton *fb = hb->fb; 977 svn_wc__db_t *db = fb->edit_baton->db; 978 svn_error_t *err; 979 980 /* Apply this window. We may be done at that point. */ 981 err = hb->apply_handler(window, hb->apply_baton); 982 if (window != NULL && !err) 983 return SVN_NO_ERROR; 984 985 if (hb->expected_source_checksum) 986 { 987 /* Close the stream to calculate HB->actual_source_md5_checksum. */ 988 svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream); 989 990 if (!err2) 991 { 992 SVN_ERR_ASSERT(hb->expected_source_checksum->kind == 993 hb->actual_source_checksum->kind); 994 995 if (!svn_checksum_match(hb->expected_source_checksum, 996 hb->actual_source_checksum)) 997 { 998 err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err, 999 _("Checksum mismatch while updating '%s':\n" 1000 " expected: %s\n" 1001 " actual: %s\n"), 1002 svn_dirent_local_style(fb->local_abspath, hb->pool), 1003 svn_checksum_to_cstring(hb->expected_source_checksum, 1004 hb->pool), 1005 svn_checksum_to_cstring(hb->actual_source_checksum, 1006 hb->pool)); 1007 } 1008 } 1009 1010 err = svn_error_compose_create(err, err2); 1011 } 1012 1013 if (err) 1014 { 1015 /* We failed to apply the delta; clean up the temporary file if it 1016 already created by lazy_open_target(). */ 1017 if (hb->new_text_base_tmp_abspath) 1018 { 1019 svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, 1020 TRUE, hb->pool)); 1021 } 1022 } 1023 else 1024 { 1025 /* Tell the file baton about the new text base's checksums. */ 1026 fb->new_text_base_md5_checksum = 1027 svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool); 1028 fb->new_text_base_sha1_checksum = 1029 svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool); 1030 1031 /* Store the new pristine text in the pristine store now. Later, in a 1032 single transaction we will update the BASE_NODE to include a 1033 reference to this pristine text's checksum. */ 1034 SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath, 1035 fb->new_text_base_sha1_checksum, 1036 fb->new_text_base_md5_checksum, 1037 hb->pool)); 1038 } 1039 1040 svn_pool_destroy(hb->pool); 1041 1042 return err; 1043} 1044 1045 1046/* Find the last-change info within ENTRY_PROPS, and return then in the 1047 CHANGED_* parameters. Each parameter will be initialized to its "none" 1048 value, and will contain the relavent info if found. 1049 1050 CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be 1051 used for some temporary allocations. 1052*/ 1053static svn_error_t * 1054accumulate_last_change(svn_revnum_t *changed_rev, 1055 apr_time_t *changed_date, 1056 const char **changed_author, 1057 const apr_array_header_t *entry_props, 1058 apr_pool_t *result_pool, 1059 apr_pool_t *scratch_pool) 1060{ 1061 int i; 1062 1063 *changed_rev = SVN_INVALID_REVNUM; 1064 *changed_date = 0; 1065 *changed_author = NULL; 1066 1067 for (i = 0; i < entry_props->nelts; ++i) 1068 { 1069 const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t); 1070 1071 /* A prop value of NULL means the information was not 1072 available. We don't remove this field from the entries 1073 file; we have convention just leave it empty. So let's 1074 just skip those entry props that have no values. */ 1075 if (! prop->value) 1076 continue; 1077 1078 if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR)) 1079 *changed_author = apr_pstrdup(result_pool, prop->value->data); 1080 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV)) 1081 { 1082 apr_int64_t rev; 1083 SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data)); 1084 *changed_rev = (svn_revnum_t)rev; 1085 } 1086 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE)) 1087 SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data, 1088 scratch_pool)); 1089 1090 /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID 1091 property here. */ 1092 } 1093 1094 return SVN_NO_ERROR; 1095} 1096 1097 1098/* Join ADD_PATH to BASE_PATH. If ADD_PATH is absolute, or if any ".." 1099 * component of it resolves to a path above BASE_PATH, then return 1100 * SVN_ERR_WC_OBSTRUCTED_UPDATE. 1101 * 1102 * This is to prevent the situation where the repository contains, 1103 * say, "..\nastyfile". Although that's perfectly legal on some 1104 * systems, when checked out onto Win32 it would cause "nastyfile" to 1105 * be created in the parent of the current edit directory. 1106 * 1107 * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846) 1108 */ 1109static svn_error_t * 1110path_join_under_root(const char **result_path, 1111 const char *base_path, 1112 const char *add_path, 1113 apr_pool_t *pool) 1114{ 1115 svn_boolean_t under_root; 1116 1117 SVN_ERR(svn_dirent_is_under_root(&under_root, 1118 result_path, base_path, add_path, pool)); 1119 1120 if (! under_root) 1121 { 1122 return svn_error_createf( 1123 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 1124 _("Path '%s' is not in the working copy"), 1125 svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool), 1126 pool)); 1127 } 1128 1129 /* This catches issue #3288 */ 1130 if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0) 1131 { 1132 return svn_error_createf( 1133 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 1134 _("'%s' is not valid as filename in directory '%s'"), 1135 svn_dirent_local_style(add_path, pool), 1136 svn_dirent_local_style(base_path, pool)); 1137 } 1138 1139 return SVN_NO_ERROR; 1140} 1141 1142 1143/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/ 1144 1145/* An svn_delta_editor_t function. */ 1146static svn_error_t * 1147set_target_revision(void *edit_baton, 1148 svn_revnum_t target_revision, 1149 apr_pool_t *pool) 1150{ 1151 struct edit_baton *eb = edit_baton; 1152 1153 *(eb->target_revision) = target_revision; 1154 return SVN_NO_ERROR; 1155} 1156 1157static svn_error_t * 1158check_tree_conflict(svn_skel_t **pconflict, 1159 struct edit_baton *eb, 1160 const char *local_abspath, 1161 svn_wc__db_status_t working_status, 1162 svn_boolean_t exists_in_repos, 1163 svn_node_kind_t expected_kind, 1164 svn_wc_conflict_action_t action, 1165 apr_pool_t *result_pool, 1166 apr_pool_t *scratch_pool); 1167 1168/* An svn_delta_editor_t function. */ 1169static svn_error_t * 1170open_root(void *edit_baton, 1171 svn_revnum_t base_revision, /* This is ignored in co */ 1172 apr_pool_t *pool, 1173 void **dir_baton) 1174{ 1175 struct edit_baton *eb = edit_baton; 1176 struct dir_baton *db; 1177 svn_boolean_t already_conflicted, conflict_ignored; 1178 svn_error_t *err; 1179 svn_wc__db_status_t status; 1180 svn_wc__db_status_t base_status; 1181 svn_node_kind_t kind; 1182 svn_boolean_t have_work; 1183 1184 /* Note that something interesting is actually happening in this 1185 edit run. */ 1186 eb->root_opened = TRUE; 1187 1188 SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool)); 1189 *dir_baton = db; 1190 1191 err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored, 1192 eb->db, db->local_abspath, pool); 1193 1194 if (err) 1195 { 1196 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1197 return svn_error_trace(err); 1198 1199 svn_error_clear(err); 1200 already_conflicted = conflict_ignored = FALSE; 1201 } 1202 else if (already_conflicted) 1203 { 1204 /* Record a skip of both the anchor and target in the skipped tree 1205 as the anchor itself might not be updated */ 1206 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 1207 SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool)); 1208 1209 db->skip_this = TRUE; 1210 db->already_notified = TRUE; 1211 1212 /* Notify that we skipped the target, while we actually skipped 1213 the anchor */ 1214 do_notification(eb, eb->target_abspath, svn_node_unknown, 1215 svn_wc_notify_skip_conflicted, pool); 1216 1217 return SVN_NO_ERROR; 1218 } 1219 1220 1221 SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision, 1222 &db->old_repos_relpath, NULL, NULL, 1223 &db->changed_rev, &db->changed_date, 1224 &db->changed_author, &db->ambient_depth, 1225 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1226 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1227 NULL, NULL, &have_work, 1228 eb->db, db->local_abspath, 1229 db->pool, pool)); 1230 1231 if (conflict_ignored) 1232 { 1233 db->shadowed = TRUE; 1234 } 1235 else if (have_work) 1236 { 1237 const char *move_src_root_abspath; 1238 1239 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath, 1240 NULL, eb->db, db->local_abspath, 1241 pool, pool)); 1242 if (move_src_root_abspath || *eb->target_basename == '\0') 1243 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, 1244 &db->old_revision, 1245 &db->old_repos_relpath, NULL, NULL, 1246 &db->changed_rev, &db->changed_date, 1247 &db->changed_author, 1248 &db->ambient_depth, 1249 NULL, NULL, NULL, NULL, NULL, NULL, 1250 eb->db, db->local_abspath, 1251 db->pool, pool)); 1252 1253 if (move_src_root_abspath) 1254 { 1255 /* This is an update anchored inside a move. We need to 1256 raise a move-edit tree-conflict on the move root to 1257 update the move destination. */ 1258 svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool); 1259 1260 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 1261 tree_conflict, eb->db, move_src_root_abspath, 1262 svn_wc_conflict_reason_moved_away, 1263 svn_wc_conflict_action_edit, 1264 move_src_root_abspath, pool, pool)); 1265 1266 if (strcmp(db->local_abspath, move_src_root_abspath)) 1267 { 1268 /* We are raising the tree-conflict on some parent of 1269 the edit root, we won't be handling that path again 1270 so raise the conflict now. */ 1271 SVN_ERR(complete_conflict(tree_conflict, eb, 1272 move_src_root_abspath, 1273 db->old_repos_relpath, 1274 db->old_revision, db->new_relpath, 1275 svn_node_dir, svn_node_dir, 1276 pool, pool)); 1277 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, 1278 move_src_root_abspath, 1279 tree_conflict, 1280 NULL, pool)); 1281 do_notification(eb, move_src_root_abspath, svn_node_dir, 1282 svn_wc_notify_tree_conflict, pool); 1283 } 1284 else 1285 db->edit_conflict = tree_conflict; 1286 } 1287 1288 db->shadowed = TRUE; /* Needed for the close_directory() on the root, to 1289 make sure it doesn't use the ACTUAL tree */ 1290 } 1291 else 1292 base_status = status; 1293 1294 if (*eb->target_basename == '\0') 1295 { 1296 /* For an update with a NULL target, this is equivalent to open_dir(): */ 1297 1298 db->was_incomplete = (base_status == svn_wc__db_status_incomplete); 1299 1300 /* ### TODO: Add some tree conflict and obstruction detection, etc. like 1301 open_directory() does. 1302 (or find a way to reuse that code here) 1303 1304 ### BH 2013: I don't think we need all of the detection here, as the 1305 user explicitly asked to update this node. So we don't 1306 have to tell that it is a local replacement/delete. 1307 */ 1308 1309 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, 1310 db->local_abspath, 1311 db->new_relpath, 1312 *eb->target_revision, 1313 pool)); 1314 } 1315 1316 return SVN_NO_ERROR; 1317} 1318 1319 1320/* ===================================================================== */ 1321/* Checking for local modifications. */ 1322 1323/* A baton for use with modcheck_found_entry(). */ 1324typedef struct modcheck_baton_t { 1325 svn_wc__db_t *db; /* wc_db to access nodes */ 1326 svn_boolean_t found_mod; /* whether a modification has been found */ 1327 svn_boolean_t found_not_delete; /* Found a not-delete modification */ 1328} modcheck_baton_t; 1329 1330/* An implementation of svn_wc_status_func4_t. */ 1331static svn_error_t * 1332modcheck_callback(void *baton, 1333 const char *local_abspath, 1334 const svn_wc_status3_t *status, 1335 apr_pool_t *scratch_pool) 1336{ 1337 modcheck_baton_t *mb = baton; 1338 1339 switch (status->node_status) 1340 { 1341 case svn_wc_status_normal: 1342 case svn_wc_status_incomplete: 1343 case svn_wc_status_ignored: 1344 case svn_wc_status_none: 1345 case svn_wc_status_unversioned: 1346 case svn_wc_status_external: 1347 break; 1348 1349 case svn_wc_status_deleted: 1350 mb->found_mod = TRUE; 1351 break; 1352 1353 case svn_wc_status_missing: 1354 case svn_wc_status_obstructed: 1355 mb->found_mod = TRUE; 1356 mb->found_not_delete = TRUE; 1357 /* Exit from the status walker: We know what we want to know */ 1358 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); 1359 1360 default: 1361 case svn_wc_status_added: 1362 case svn_wc_status_replaced: 1363 case svn_wc_status_modified: 1364 mb->found_mod = TRUE; 1365 mb->found_not_delete = TRUE; 1366 /* Exit from the status walker: We know what we want to know */ 1367 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); 1368 } 1369 1370 return SVN_NO_ERROR; 1371} 1372 1373 1374/* Set *MODIFIED to true iff there are any local modifications within the 1375 * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED 1376 * is set to true and all the local modifications were deletes then set 1377 * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH 1378 * may be a file or a directory. */ 1379svn_error_t * 1380svn_wc__node_has_local_mods(svn_boolean_t *modified, 1381 svn_boolean_t *all_edits_are_deletes, 1382 svn_wc__db_t *db, 1383 const char *local_abspath, 1384 svn_cancel_func_t cancel_func, 1385 void *cancel_baton, 1386 apr_pool_t *scratch_pool) 1387{ 1388 modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE }; 1389 svn_error_t *err; 1390 1391 modcheck_baton.db = db; 1392 1393 /* Walk the WC tree for status with depth infinity, looking for any local 1394 * modifications. If it's a "sparse" directory, that's OK: there can be 1395 * no local mods in the pieces that aren't present in the WC. */ 1396 1397 err = svn_wc__internal_walk_status(db, local_abspath, 1398 svn_depth_infinity, 1399 FALSE, FALSE, FALSE, NULL, 1400 modcheck_callback, &modcheck_baton, 1401 cancel_func, cancel_baton, 1402 scratch_pool); 1403 1404 if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) 1405 svn_error_clear(err); 1406 else 1407 SVN_ERR(err); 1408 1409 *modified = modcheck_baton.found_mod; 1410 *all_edits_are_deletes = (modcheck_baton.found_mod 1411 && !modcheck_baton.found_not_delete); 1412 1413 return SVN_NO_ERROR; 1414} 1415 1416/* Indicates an unset svn_wc_conflict_reason_t. */ 1417#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1) 1418 1419/* Check whether the incoming change ACTION on FULL_PATH would conflict with 1420 * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with 1421 * LOCAL_ABSPATH as the victim. 1422 * 1423 * The edit baton EB gives information including whether the operation is 1424 * an update or a switch. 1425 * 1426 * WORKING_STATUS is the current node status of LOCAL_ABSPATH 1427 * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists 1428 * for this node. In that case the on disk type is compared to EXPECTED_KIND. 1429 * 1430 * If a tree conflict reason was found for the incoming action, the resulting 1431 * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL, 1432 * while *PCONFLICT is always overwritten. 1433 * 1434 * The tree conflict is allocated in RESULT_POOL. Temporary allocations use 1435 * SCRATCH_POOL. 1436 */ 1437static svn_error_t * 1438check_tree_conflict(svn_skel_t **pconflict, 1439 struct edit_baton *eb, 1440 const char *local_abspath, 1441 svn_wc__db_status_t working_status, 1442 svn_boolean_t exists_in_repos, 1443 svn_node_kind_t expected_kind, 1444 svn_wc_conflict_action_t action, 1445 apr_pool_t *result_pool, 1446 apr_pool_t *scratch_pool) 1447{ 1448 svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE; 1449 svn_boolean_t modified = FALSE; 1450 svn_boolean_t all_mods_are_deletes = FALSE; 1451 const char *move_src_op_root_abspath = NULL; 1452 1453 *pconflict = NULL; 1454 1455 /* Find out if there are any local changes to this node that may 1456 * be the "reason" of a tree-conflict with the incoming "action". */ 1457 switch (working_status) 1458 { 1459 case svn_wc__db_status_added: 1460 case svn_wc__db_status_moved_here: 1461 case svn_wc__db_status_copied: 1462 if (!exists_in_repos) 1463 { 1464 /* The node is locally added, and it did not exist before. This 1465 * is an 'update', so the local add can only conflict with an 1466 * incoming 'add'. In fact, if we receive anything else than an 1467 * svn_wc_conflict_action_add (which includes 'added', 1468 * 'copied-here' and 'moved-here') during update on a node that 1469 * did not exist before, then something is very wrong. 1470 * Note that if there was no action on the node, this code 1471 * would not have been called in the first place. */ 1472 SVN_ERR_ASSERT(action == svn_wc_conflict_action_add); 1473 1474 /* Scan the addition in case our caller didn't. */ 1475 if (working_status == svn_wc__db_status_added) 1476 SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL, 1477 NULL, NULL, NULL, NULL, 1478 NULL, NULL, 1479 eb->db, local_abspath, 1480 scratch_pool, scratch_pool)); 1481 1482 if (working_status == svn_wc__db_status_moved_here) 1483 reason = svn_wc_conflict_reason_moved_here; 1484 else 1485 reason = svn_wc_conflict_reason_added; 1486 } 1487 else 1488 { 1489 /* The node is locally replaced but could also be moved-away. */ 1490 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL, 1491 &move_src_op_root_abspath, 1492 eb->db, local_abspath, 1493 scratch_pool, scratch_pool)); 1494 if (move_src_op_root_abspath) 1495 reason = svn_wc_conflict_reason_moved_away; 1496 else 1497 reason = svn_wc_conflict_reason_replaced; 1498 } 1499 break; 1500 1501 1502 case svn_wc__db_status_deleted: 1503 { 1504 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL, 1505 &move_src_op_root_abspath, 1506 eb->db, local_abspath, 1507 scratch_pool, scratch_pool)); 1508 if (move_src_op_root_abspath) 1509 reason = svn_wc_conflict_reason_moved_away; 1510 else 1511 reason = svn_wc_conflict_reason_deleted; 1512 } 1513 break; 1514 1515 case svn_wc__db_status_incomplete: 1516 /* We used svn_wc__db_read_info(), so 'incomplete' means 1517 * - there is no node in the WORKING tree 1518 * - a BASE node is known to exist 1519 * So the node exists and is essentially 'normal'. We still need to 1520 * check prop and text mods, and those checks will retrieve the 1521 * missing information (hopefully). */ 1522 case svn_wc__db_status_normal: 1523 if (action == svn_wc_conflict_action_edit) 1524 { 1525 /* An edit onto a local edit or onto *no* local changes is no 1526 * tree-conflict. (It's possibly a text- or prop-conflict, 1527 * but we don't handle those here.) 1528 * 1529 * Except when there is a local obstruction 1530 */ 1531 if (exists_in_repos) 1532 { 1533 svn_node_kind_t disk_kind; 1534 1535 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, 1536 scratch_pool)); 1537 1538 if (disk_kind != expected_kind && disk_kind != svn_node_none) 1539 { 1540 reason = svn_wc_conflict_reason_obstructed; 1541 break; 1542 } 1543 1544 } 1545 return SVN_NO_ERROR; 1546 } 1547 1548 /* Replace is handled as delete and then specifically in 1549 add_directory() and add_file(), so we only expect deletes here */ 1550 SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete); 1551 1552 /* Check if the update wants to delete or replace a locally 1553 * modified node. */ 1554 1555 1556 /* Do a deep tree detection of local changes. The update editor will 1557 * not visit the subdirectories of a directory that it wants to delete. 1558 * Therefore, we need to start a separate crawl here. */ 1559 1560 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes, 1561 eb->db, local_abspath, 1562 eb->cancel_func, eb->cancel_baton, 1563 scratch_pool)); 1564 1565 if (modified) 1566 { 1567 if (all_mods_are_deletes) 1568 reason = svn_wc_conflict_reason_deleted; 1569 else 1570 reason = svn_wc_conflict_reason_edited; 1571 } 1572 break; 1573 1574 case svn_wc__db_status_server_excluded: 1575 /* Not allowed to view the node. Not allowed to report tree 1576 * conflicts. */ 1577 case svn_wc__db_status_excluded: 1578 /* Locally marked as excluded. No conflicts wanted. */ 1579 case svn_wc__db_status_not_present: 1580 /* A committed delete (but parent not updated). The delete is 1581 committed, so no conflict possible during update. */ 1582 return SVN_NO_ERROR; 1583 1584 case svn_wc__db_status_base_deleted: 1585 /* An internal status. Should never show up here. */ 1586 SVN_ERR_MALFUNCTION(); 1587 break; 1588 1589 } 1590 1591 if (reason == SVN_WC_CONFLICT_REASON_NONE) 1592 /* No conflict with the current action. */ 1593 return SVN_NO_ERROR; 1594 1595 1596 /* Sanity checks. Note that if there was no action on the node, this function 1597 * would not have been called in the first place.*/ 1598 if (reason == svn_wc_conflict_reason_edited 1599 || reason == svn_wc_conflict_reason_obstructed 1600 || reason == svn_wc_conflict_reason_deleted 1601 || reason == svn_wc_conflict_reason_moved_away 1602 || reason == svn_wc_conflict_reason_replaced) 1603 { 1604 /* When the node existed before (it was locally deleted, replaced or 1605 * edited), then 'update' cannot add it "again". So it can only send 1606 * _action_edit, _delete or _replace. */ 1607 if (action != svn_wc_conflict_action_edit 1608 && action != svn_wc_conflict_action_delete 1609 && action != svn_wc_conflict_action_replace) 1610 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 1611 _("Unexpected attempt to add a node at path '%s'"), 1612 svn_dirent_local_style(local_abspath, scratch_pool)); 1613 } 1614 else if (reason == svn_wc_conflict_reason_added || 1615 reason == svn_wc_conflict_reason_moved_here) 1616 { 1617 /* When the node did not exist before (it was locally added), 1618 * then 'update' cannot want to modify it in any way. 1619 * It can only send _action_add. */ 1620 if (action != svn_wc_conflict_action_add) 1621 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 1622 _("Unexpected attempt to edit, delete, or replace " 1623 "a node at path '%s'"), 1624 svn_dirent_local_style(local_abspath, scratch_pool)); 1625 1626 } 1627 1628 1629 /* A conflict was detected. Create a conflict skel to record it. */ 1630 *pconflict = svn_wc__conflict_skel_create(result_pool); 1631 1632 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict, 1633 eb->db, local_abspath, 1634 reason, 1635 action, 1636 move_src_op_root_abspath, 1637 result_pool, scratch_pool)); 1638 1639 return SVN_NO_ERROR; 1640} 1641 1642 1643/* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is 1644 * not a moved-away-edit conflict, set *CONFLICTED to TRUE. Otherwise 1645 * set *CONFLICTED to FALSE. 1646 */ 1647static svn_error_t * 1648already_in_a_tree_conflict(svn_boolean_t *conflicted, 1649 svn_boolean_t *ignored, 1650 svn_wc__db_t *db, 1651 const char *local_abspath, 1652 apr_pool_t *scratch_pool) 1653{ 1654 const char *ancestor_abspath = local_abspath; 1655 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1656 1657 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 1658 1659 *conflicted = *ignored = FALSE; 1660 1661 while (TRUE) 1662 { 1663 svn_boolean_t is_wc_root; 1664 1665 svn_pool_clear(iterpool); 1666 1667 SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db, 1668 ancestor_abspath, TRUE, 1669 scratch_pool)); 1670 if (*conflicted || *ignored) 1671 break; 1672 1673 SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath, 1674 iterpool)); 1675 if (is_wc_root) 1676 break; 1677 1678 ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool); 1679 } 1680 1681 svn_pool_destroy(iterpool); 1682 1683 return SVN_NO_ERROR; 1684} 1685 1686/* Temporary helper until the new conflict handling is in place */ 1687static svn_error_t * 1688node_already_conflicted(svn_boolean_t *conflicted, 1689 svn_boolean_t *conflict_ignored, 1690 svn_wc__db_t *db, 1691 const char *local_abspath, 1692 apr_pool_t *scratch_pool) 1693{ 1694 SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db, 1695 local_abspath, FALSE, 1696 scratch_pool)); 1697 1698 return SVN_NO_ERROR; 1699} 1700 1701 1702/* An svn_delta_editor_t function. */ 1703static svn_error_t * 1704delete_entry(const char *path, 1705 svn_revnum_t revision, 1706 void *parent_baton, 1707 apr_pool_t *pool) 1708{ 1709 struct dir_baton *pb = parent_baton; 1710 struct edit_baton *eb = pb->edit_baton; 1711 const char *base = svn_relpath_basename(path, NULL); 1712 const char *local_abspath; 1713 const char *repos_relpath; 1714 svn_node_kind_t kind, base_kind; 1715 svn_revnum_t old_revision; 1716 svn_boolean_t conflicted; 1717 svn_boolean_t have_work; 1718 svn_skel_t *tree_conflict = NULL; 1719 svn_wc__db_status_t status; 1720 svn_wc__db_status_t base_status; 1721 apr_pool_t *scratch_pool; 1722 svn_boolean_t deleting_target; 1723 svn_boolean_t deleting_switched; 1724 svn_boolean_t keep_as_working = FALSE; 1725 svn_boolean_t queue_deletes = TRUE; 1726 1727 if (pb->skip_this) 1728 return SVN_NO_ERROR; 1729 1730 scratch_pool = svn_pool_create(pb->pool); 1731 1732 SVN_ERR(mark_directory_edited(pb, scratch_pool)); 1733 1734 SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base, 1735 scratch_pool)); 1736 1737 deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0); 1738 1739 /* Detect obstructing working copies */ 1740 { 1741 svn_boolean_t is_root; 1742 1743 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath, 1744 scratch_pool)); 1745 1746 if (is_root) 1747 { 1748 /* Just skip this node; a future update will handle it */ 1749 SVN_ERR(remember_skipped_tree(eb, local_abspath, pool)); 1750 do_notification(eb, local_abspath, svn_node_unknown, 1751 svn_wc_notify_update_skip_obstruction, scratch_pool); 1752 1753 svn_pool_destroy(scratch_pool); 1754 1755 return SVN_NO_ERROR; 1756 } 1757 } 1758 1759 SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath, 1760 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1761 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1762 &conflicted, NULL, NULL, NULL, 1763 NULL, NULL, &have_work, 1764 eb->db, local_abspath, 1765 scratch_pool, scratch_pool)); 1766 1767 if (!have_work) 1768 { 1769 base_status = status; 1770 base_kind = kind; 1771 } 1772 else 1773 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision, 1774 &repos_relpath, 1775 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1776 NULL, NULL, NULL, NULL, NULL, 1777 eb->db, local_abspath, 1778 scratch_pool, scratch_pool)); 1779 1780 if (pb->old_repos_relpath && repos_relpath) 1781 { 1782 const char *expected_name; 1783 1784 expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath, 1785 repos_relpath); 1786 1787 deleting_switched = (!expected_name || strcmp(expected_name, base) != 0); 1788 } 1789 else 1790 deleting_switched = FALSE; 1791 1792 /* Is this path a conflict victim? */ 1793 if (pb->shadowed) 1794 conflicted = FALSE; /* Conflict applies to WORKING */ 1795 else if (conflicted) 1796 SVN_ERR(node_already_conflicted(&conflicted, NULL, 1797 eb->db, local_abspath, scratch_pool)); 1798 if (conflicted) 1799 { 1800 SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool)); 1801 1802 do_notification(eb, local_abspath, svn_node_unknown, 1803 svn_wc_notify_skip_conflicted, 1804 scratch_pool); 1805 1806 svn_pool_destroy(scratch_pool); 1807 1808 return SVN_NO_ERROR; 1809 } 1810 1811 1812 /* Receive the remote removal of excluded/server-excluded/not present node. 1813 Do not notify, but perform the change even when the node is shadowed */ 1814 if (base_status == svn_wc__db_status_not_present 1815 || base_status == svn_wc__db_status_excluded 1816 || base_status == svn_wc__db_status_server_excluded) 1817 { 1818 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, 1819 FALSE /* keep_as_working */, 1820 FALSE /* queue_deletes */, 1821 FALSE /* remove_locks */, 1822 SVN_INVALID_REVNUM /* not_present_rev */, 1823 NULL, NULL, 1824 scratch_pool)); 1825 1826 if (deleting_target) 1827 eb->target_deleted = TRUE; 1828 1829 svn_pool_destroy(scratch_pool); 1830 1831 return SVN_NO_ERROR; 1832 } 1833 1834 /* Is this path the victim of a newly-discovered tree conflict? If so, 1835 * remember it and notify the client. Then (if it was existing and 1836 * modified), re-schedule the node to be added back again, as a (modified) 1837 * copy of the previous base version. */ 1838 1839 /* Check for conflicts only when we haven't already recorded 1840 * a tree-conflict on a parent node. */ 1841 if (!pb->shadowed && !pb->edit_obstructed) 1842 { 1843 SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, 1844 status, TRUE, 1845 (kind == svn_node_dir) 1846 ? svn_node_dir 1847 : svn_node_file, 1848 svn_wc_conflict_action_delete, 1849 pb->pool, scratch_pool)); 1850 } 1851 else 1852 queue_deletes = FALSE; /* There is no in-wc representation */ 1853 1854 if (tree_conflict != NULL) 1855 { 1856 svn_wc_conflict_reason_t reason; 1857 /* When we raise a tree conflict on a node, we don't want to mark the 1858 * node as skipped, to allow a replacement to continue doing at least 1859 * a bit of its work (possibly adding a not present node, for the 1860 * next update) */ 1861 if (!pb->deletion_conflicts) 1862 pb->deletion_conflicts = apr_hash_make(pb->pool); 1863 1864 svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base), 1865 tree_conflict); 1866 1867 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 1868 eb->db, local_abspath, 1869 tree_conflict, 1870 scratch_pool, scratch_pool)); 1871 1872 if (reason == svn_wc_conflict_reason_edited 1873 || reason == svn_wc_conflict_reason_obstructed) 1874 { 1875 /* The item exists locally and has some sort of local mod. 1876 * It no longer exists in the repository at its target URL@REV. 1877 * 1878 * To prepare the "accept mine" resolution for the tree conflict, 1879 * we must schedule the existing content for re-addition as a copy 1880 * of what it was, but with its local modifications preserved. */ 1881 keep_as_working = TRUE; 1882 1883 /* Fall through to remove the BASE_NODEs properly, with potentially 1884 keeping a not-present marker */ 1885 } 1886 else if (reason == svn_wc_conflict_reason_deleted 1887 || reason == svn_wc_conflict_reason_moved_away 1888 || reason == svn_wc_conflict_reason_replaced) 1889 { 1890 /* The item does not exist locally because it was already shadowed. 1891 * We must complete the deletion, leaving the tree conflict info 1892 * as the only difference from a normal deletion. */ 1893 1894 /* Fall through to the normal "delete" code path. */ 1895 } 1896 else 1897 SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */ 1898 } 1899 1900 SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath, 1901 old_revision, NULL, 1902 (kind == svn_node_dir) 1903 ? svn_node_dir 1904 : svn_node_file, 1905 svn_node_none, 1906 pb->pool, scratch_pool)); 1907 1908 /* Issue a wq operation to delete the BASE_NODE data and to delete actual 1909 nodes based on that from disk, but leave any WORKING_NODEs on disk. 1910 1911 Local modifications are already turned into copies at this point. 1912 1913 If the thing being deleted is the *target* of this update, then 1914 we need to recreate a 'deleted' entry, so that the parent can give 1915 accurate reports about itself in the future. */ 1916 if (! deleting_target && ! deleting_switched) 1917 { 1918 /* Delete, and do not leave a not-present node. */ 1919 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, 1920 keep_as_working, queue_deletes, FALSE, 1921 SVN_INVALID_REVNUM /* not_present_rev */, 1922 tree_conflict, NULL, 1923 scratch_pool)); 1924 } 1925 else 1926 { 1927 /* Delete, leaving a not-present node. */ 1928 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, 1929 keep_as_working, queue_deletes, FALSE, 1930 *eb->target_revision, 1931 tree_conflict, NULL, 1932 scratch_pool)); 1933 if (deleting_target) 1934 eb->target_deleted = TRUE; 1935 else 1936 { 1937 /* Don't remove the not-present marker at the final bump */ 1938 SVN_ERR(remember_skipped_tree(eb, local_abspath, pool)); 1939 } 1940 } 1941 1942 SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath, 1943 eb->cancel_func, eb->cancel_baton, 1944 scratch_pool)); 1945 1946 /* Notify. */ 1947 if (tree_conflict) 1948 { 1949 if (eb->conflict_func) 1950 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, 1951 tree_conflict, 1952 NULL /* merge_options */, 1953 eb->conflict_func, 1954 eb->conflict_baton, 1955 eb->cancel_func, 1956 eb->cancel_baton, 1957 scratch_pool)); 1958 do_notification(eb, local_abspath, svn_node_unknown, 1959 svn_wc_notify_tree_conflict, scratch_pool); 1960 } 1961 else 1962 { 1963 svn_wc_notify_action_t action = svn_wc_notify_update_delete; 1964 svn_node_kind_t node_kind; 1965 1966 if (pb->shadowed || pb->edit_obstructed) 1967 action = svn_wc_notify_update_shadowed_delete; 1968 1969 if (kind == svn_node_dir) 1970 node_kind = svn_node_dir; 1971 else 1972 node_kind = svn_node_file; 1973 1974 do_notification(eb, local_abspath, node_kind, action, scratch_pool); 1975 } 1976 1977 svn_pool_destroy(scratch_pool); 1978 1979 return SVN_NO_ERROR; 1980} 1981 1982/* An svn_delta_editor_t function. */ 1983static svn_error_t * 1984add_directory(const char *path, 1985 void *parent_baton, 1986 const char *copyfrom_path, 1987 svn_revnum_t copyfrom_rev, 1988 apr_pool_t *pool, 1989 void **child_baton) 1990{ 1991 struct dir_baton *pb = parent_baton; 1992 struct edit_baton *eb = pb->edit_baton; 1993 struct dir_baton *db; 1994 svn_node_kind_t kind; 1995 svn_wc__db_status_t status; 1996 svn_node_kind_t wc_kind; 1997 svn_boolean_t conflicted; 1998 svn_boolean_t conflict_ignored = FALSE; 1999 svn_boolean_t versioned_locally_and_present; 2000 svn_skel_t *tree_conflict = NULL; 2001 svn_error_t *err; 2002 2003 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev))); 2004 2005 SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool)); 2006 *child_baton = db; 2007 2008 if (db->skip_this) 2009 return SVN_NO_ERROR; 2010 2011 SVN_ERR(mark_directory_edited(db, pool)); 2012 2013 if (strcmp(eb->target_abspath, db->local_abspath) == 0) 2014 { 2015 /* The target of the edit is being added, give it the requested 2016 depth of the edit (but convert svn_depth_unknown to 2017 svn_depth_infinity). */ 2018 db->ambient_depth = (eb->requested_depth == svn_depth_unknown) 2019 ? svn_depth_infinity : eb->requested_depth; 2020 } 2021 else if (eb->requested_depth == svn_depth_immediates 2022 || (eb->requested_depth == svn_depth_unknown 2023 && pb->ambient_depth == svn_depth_immediates)) 2024 { 2025 db->ambient_depth = svn_depth_empty; 2026 } 2027 else 2028 { 2029 db->ambient_depth = svn_depth_infinity; 2030 } 2031 2032 /* It may not be named the same as the administrative directory. */ 2033 if (svn_wc_is_adm_dir(db->name, pool)) 2034 return svn_error_createf( 2035 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 2036 _("Failed to add directory '%s': object of the same name as the " 2037 "administrative directory"), 2038 svn_dirent_local_style(db->local_abspath, pool)); 2039 2040 SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool)); 2041 2042 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, 2043 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2044 NULL, NULL, NULL, NULL, NULL, 2045 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, 2046 eb->db, db->local_abspath, db->pool, db->pool); 2047 if (err) 2048 { 2049 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2050 return svn_error_trace(err); 2051 2052 svn_error_clear(err); 2053 wc_kind = svn_node_unknown; 2054 status = svn_wc__db_status_normal; 2055 conflicted = FALSE; 2056 2057 versioned_locally_and_present = FALSE; 2058 } 2059 else if (wc_kind == svn_node_dir 2060 && status == svn_wc__db_status_normal) 2061 { 2062 /* !! We found the root of a separate working copy obstructing the wc !! 2063 2064 If the directory would be part of our own working copy then 2065 we wouldn't have been called as an add_directory(). 2066 2067 The only thing we can do is add a not-present node, to allow 2068 a future update to bring in the new files when the problem is 2069 resolved. Note that svn_wc__db_base_add_not_present_node() 2070 explicitly adds the node into the parent's node database. */ 2071 2072 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath, 2073 db->new_relpath, 2074 eb->repos_root, 2075 eb->repos_uuid, 2076 *eb->target_revision, 2077 svn_node_file, 2078 NULL, NULL, 2079 pool)); 2080 2081 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2082 db->skip_this = TRUE; 2083 db->already_notified = TRUE; 2084 2085 do_notification(eb, db->local_abspath, svn_node_dir, 2086 svn_wc_notify_update_skip_obstruction, pool); 2087 2088 return SVN_NO_ERROR; 2089 } 2090 else if (status == svn_wc__db_status_normal 2091 && (wc_kind == svn_node_file 2092 || wc_kind == svn_node_symlink)) 2093 { 2094 /* We found a file external occupating the place we need in BASE. 2095 2096 We can't add a not-present node in this case as that would overwrite 2097 the file external. Luckily the file external itself stops us from 2098 forgetting a child of this parent directory like an obstructing 2099 working copy would. 2100 2101 The reason we get here is that the adm crawler doesn't report 2102 file externals. 2103 */ 2104 2105 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2106 db->skip_this = TRUE; 2107 db->already_notified = TRUE; 2108 2109 do_notification(eb, db->local_abspath, svn_node_file, 2110 svn_wc_notify_update_skip_obstruction, pool); 2111 2112 return SVN_NO_ERROR; 2113 } 2114 else if (wc_kind == svn_node_unknown) 2115 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ 2116 else 2117 versioned_locally_and_present = IS_NODE_PRESENT(status); 2118 2119 /* Is this path a conflict victim? */ 2120 if (conflicted) 2121 { 2122 if (pb->deletion_conflicts) 2123 tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name); 2124 2125 if (tree_conflict) 2126 { 2127 svn_wc_conflict_reason_t reason; 2128 /* So this deletion wasn't just a deletion, it is actually a 2129 replacement. Let's install a better tree conflict. */ 2130 2131 /* ### Should store the conflict in DB to allow reinstalling 2132 ### with theoretically more data in close_directory() */ 2133 2134 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 2135 eb->db, 2136 db->local_abspath, 2137 tree_conflict, 2138 db->pool, db->pool)); 2139 2140 tree_conflict = svn_wc__conflict_skel_create(db->pool); 2141 2142 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 2143 tree_conflict, 2144 eb->db, db->local_abspath, 2145 reason, svn_wc_conflict_action_replace, 2146 NULL, 2147 db->pool, db->pool)); 2148 2149 /* And now stop checking for conflicts here and just perform 2150 a shadowed update */ 2151 db->edit_conflict = tree_conflict; /* Cache for close_directory */ 2152 tree_conflict = NULL; /* No direct notification */ 2153 db->shadowed = TRUE; /* Just continue */ 2154 conflicted = FALSE; /* No skip */ 2155 } 2156 else 2157 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 2158 eb->db, db->local_abspath, pool)); 2159 } 2160 2161 /* Now the "usual" behaviour if already conflicted. Skip it. */ 2162 if (conflicted) 2163 { 2164 /* Record this conflict so that its descendants are skipped silently. */ 2165 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2166 2167 db->skip_this = TRUE; 2168 db->already_notified = TRUE; 2169 2170 /* We skip this node, but once the update completes the parent node will 2171 be updated to the new revision. So a future recursive update of the 2172 parent will not bring in this new node as the revision of the parent 2173 describes to the repository that all children are available. 2174 2175 To resolve this problem, we add a not-present node to allow bringing 2176 the node in once this conflict is resolved. 2177 2178 Note that we can safely assume that no present base node exists, 2179 because then we would not have received an add_directory. 2180 */ 2181 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath, 2182 db->new_relpath, 2183 eb->repos_root, 2184 eb->repos_uuid, 2185 *eb->target_revision, 2186 svn_node_dir, 2187 NULL, NULL, 2188 pool)); 2189 2190 /* ### TODO: Also print victim_path in the skip msg. */ 2191 do_notification(eb, db->local_abspath, svn_node_dir, 2192 svn_wc_notify_skip_conflicted, pool); 2193 return SVN_NO_ERROR; 2194 } 2195 else if (conflict_ignored) 2196 { 2197 db->shadowed = TRUE; 2198 } 2199 2200 if (db->shadowed) 2201 { 2202 /* Nothing to check; does not and will not exist in working copy */ 2203 } 2204 else if (versioned_locally_and_present) 2205 { 2206 /* What to do with a versioned or schedule-add dir: 2207 2208 A dir already added without history is OK. Set add_existed 2209 so that user notification is delayed until after any prop 2210 conflicts have been found. 2211 2212 An existing versioned dir is an error. In the future we may 2213 relax this restriction and simply update such dirs. 2214 2215 A dir added with history is a tree conflict. */ 2216 2217 svn_boolean_t local_is_non_dir; 2218 svn_wc__db_status_t add_status = svn_wc__db_status_normal; 2219 2220 /* Is the local add a copy? */ 2221 if (status == svn_wc__db_status_added) 2222 SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL, 2223 NULL, NULL, NULL, NULL, 2224 eb->db, db->local_abspath, 2225 pool, pool)); 2226 2227 2228 /* Is there *something* that is not a dir? */ 2229 local_is_non_dir = (wc_kind != svn_node_dir 2230 && status != svn_wc__db_status_deleted); 2231 2232 /* Do tree conflict checking if 2233 * - if there is a local copy. 2234 * - if this is a switch operation 2235 * - the node kinds mismatch 2236 * 2237 * During switch, local adds at the same path as incoming adds get 2238 * "lost" in that switching back to the original will no longer have the 2239 * local add. So switch always alerts the user with a tree conflict. */ 2240 if (!eb->adds_as_modification 2241 || local_is_non_dir 2242 || add_status != svn_wc__db_status_added) 2243 { 2244 SVN_ERR(check_tree_conflict(&tree_conflict, eb, 2245 db->local_abspath, 2246 status, FALSE, svn_node_none, 2247 svn_wc_conflict_action_add, 2248 pool, pool)); 2249 } 2250 2251 if (tree_conflict == NULL) 2252 db->add_existed = TRUE; /* Take over WORKING */ 2253 else 2254 db->shadowed = TRUE; /* Only update BASE */ 2255 } 2256 else if (kind != svn_node_none) 2257 { 2258 /* There's an unversioned node at this path. */ 2259 db->obstruction_found = TRUE; 2260 2261 /* Unversioned, obstructing dirs are handled by prop merge/conflict, 2262 * if unversioned obstructions are allowed. */ 2263 if (! (kind == svn_node_dir && eb->allow_unver_obstructions)) 2264 { 2265 /* Bring in the node as deleted */ /* ### Obstructed Conflict */ 2266 db->shadowed = TRUE; 2267 2268 /* Mark a conflict */ 2269 tree_conflict = svn_wc__conflict_skel_create(db->pool); 2270 2271 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 2272 tree_conflict, 2273 eb->db, db->local_abspath, 2274 svn_wc_conflict_reason_unversioned, 2275 svn_wc_conflict_action_add, NULL, 2276 db->pool, pool)); 2277 db->edit_conflict = tree_conflict; 2278 } 2279 } 2280 2281 if (tree_conflict) 2282 SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath, 2283 db->old_repos_relpath, db->old_revision, 2284 db->new_relpath, 2285 wc_kind, 2286 svn_node_dir, 2287 db->pool, pool)); 2288 2289 SVN_ERR(svn_wc__db_base_add_incomplete_directory( 2290 eb->db, db->local_abspath, 2291 db->new_relpath, 2292 eb->repos_root, 2293 eb->repos_uuid, 2294 *eb->target_revision, 2295 db->ambient_depth, 2296 (db->shadowed && db->obstruction_found), 2297 (! db->shadowed 2298 && status == svn_wc__db_status_added), 2299 tree_conflict, NULL, 2300 pool)); 2301 2302 /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just 2303 updating the DB */ 2304 if (!db->shadowed) 2305 SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool)); 2306 2307 if (tree_conflict != NULL) 2308 { 2309 if (eb->conflict_func) 2310 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, 2311 tree_conflict, 2312 NULL /* merge_options */, 2313 eb->conflict_func, 2314 eb->conflict_baton, 2315 eb->cancel_func, 2316 eb->cancel_baton, 2317 pool)); 2318 2319 db->already_notified = TRUE; 2320 do_notification(eb, db->local_abspath, svn_node_dir, 2321 svn_wc_notify_tree_conflict, pool); 2322 } 2323 2324 2325 /* If this add was obstructed by dir scheduled for addition without 2326 history let close_directory() handle the notification because there 2327 might be properties to deal with. If PATH was added inside a locally 2328 deleted tree, then suppress notification, a tree conflict was already 2329 issued. */ 2330 if (eb->notify_func && !db->already_notified && !db->add_existed) 2331 { 2332 svn_wc_notify_action_t action; 2333 2334 if (db->shadowed) 2335 action = svn_wc_notify_update_shadowed_add; 2336 else if (db->obstruction_found || db->add_existed) 2337 action = svn_wc_notify_exists; 2338 else 2339 action = svn_wc_notify_update_add; 2340 2341 db->already_notified = TRUE; 2342 2343 do_notification(eb, db->local_abspath, svn_node_dir, action, pool); 2344 } 2345 2346 return SVN_NO_ERROR; 2347} 2348 2349/* An svn_delta_editor_t function. */ 2350static svn_error_t * 2351open_directory(const char *path, 2352 void *parent_baton, 2353 svn_revnum_t base_revision, 2354 apr_pool_t *pool, 2355 void **child_baton) 2356{ 2357 struct dir_baton *db, *pb = parent_baton; 2358 struct edit_baton *eb = pb->edit_baton; 2359 svn_boolean_t have_work; 2360 svn_boolean_t conflicted; 2361 svn_boolean_t conflict_ignored = FALSE; 2362 svn_skel_t *tree_conflict = NULL; 2363 svn_wc__db_status_t status, base_status; 2364 svn_node_kind_t wc_kind; 2365 2366 SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool)); 2367 *child_baton = db; 2368 2369 if (db->skip_this) 2370 return SVN_NO_ERROR; 2371 2372 /* Detect obstructing working copies */ 2373 { 2374 svn_boolean_t is_root; 2375 2376 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath, 2377 pool)); 2378 2379 if (is_root) 2380 { 2381 /* Just skip this node; a future update will handle it */ 2382 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2383 db->skip_this = TRUE; 2384 db->already_notified = TRUE; 2385 2386 do_notification(eb, db->local_abspath, svn_node_dir, 2387 svn_wc_notify_update_skip_obstruction, pool); 2388 2389 return SVN_NO_ERROR; 2390 } 2391 } 2392 2393 /* We should have a write lock on every directory touched. */ 2394 SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool)); 2395 2396 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision, 2397 &db->old_repos_relpath, NULL, NULL, 2398 &db->changed_rev, &db->changed_date, 2399 &db->changed_author, &db->ambient_depth, 2400 NULL, NULL, NULL, NULL, 2401 NULL, NULL, NULL, NULL, NULL, NULL, 2402 &conflicted, NULL, NULL, NULL, 2403 NULL, NULL, &have_work, 2404 eb->db, db->local_abspath, 2405 db->pool, pool)); 2406 2407 if (!have_work) 2408 base_status = status; 2409 else 2410 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision, 2411 &db->old_repos_relpath, NULL, NULL, 2412 &db->changed_rev, &db->changed_date, 2413 &db->changed_author, &db->ambient_depth, 2414 NULL, NULL, NULL, NULL, NULL, NULL, 2415 eb->db, db->local_abspath, 2416 db->pool, pool)); 2417 2418 db->was_incomplete = (base_status == svn_wc__db_status_incomplete); 2419 2420 /* Is this path a conflict victim? */ 2421 if (db->shadowed) 2422 conflicted = FALSE; /* Conflict applies to WORKING */ 2423 else if (conflicted) 2424 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 2425 eb->db, db->local_abspath, pool)); 2426 if (conflicted) 2427 { 2428 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2429 2430 db->skip_this = TRUE; 2431 db->already_notified = TRUE; 2432 2433 do_notification(eb, db->local_abspath, svn_node_unknown, 2434 svn_wc_notify_skip_conflicted, pool); 2435 2436 return SVN_NO_ERROR; 2437 } 2438 else if (conflict_ignored) 2439 { 2440 db->shadowed = TRUE; 2441 } 2442 2443 /* Is this path a fresh tree conflict victim? If so, skip the tree 2444 with one notification. */ 2445 2446 /* Check for conflicts only when we haven't already recorded 2447 * a tree-conflict on a parent node. */ 2448 if (!db->shadowed) 2449 SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath, 2450 status, TRUE, svn_node_dir, 2451 svn_wc_conflict_action_edit, 2452 db->pool, pool)); 2453 2454 /* Remember the roots of any locally deleted trees. */ 2455 if (tree_conflict != NULL) 2456 { 2457 svn_wc_conflict_reason_t reason; 2458 db->edit_conflict = tree_conflict; 2459 /* Other modifications wouldn't be a tree conflict */ 2460 2461 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 2462 eb->db, db->local_abspath, 2463 tree_conflict, 2464 db->pool, db->pool)); 2465 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted 2466 || reason == svn_wc_conflict_reason_moved_away 2467 || reason == svn_wc_conflict_reason_replaced 2468 || reason == svn_wc_conflict_reason_obstructed); 2469 2470 /* Continue updating BASE */ 2471 if (reason == svn_wc_conflict_reason_obstructed) 2472 db->edit_obstructed = TRUE; 2473 else 2474 db->shadowed = TRUE; 2475 } 2476 2477 /* Mark directory as being at target_revision and URL, but incomplete. */ 2478 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath, 2479 db->new_relpath, 2480 *eb->target_revision, 2481 pool)); 2482 2483 return SVN_NO_ERROR; 2484} 2485 2486 2487/* An svn_delta_editor_t function. */ 2488static svn_error_t * 2489change_dir_prop(void *dir_baton, 2490 const char *name, 2491 const svn_string_t *value, 2492 apr_pool_t *pool) 2493{ 2494 svn_prop_t *propchange; 2495 struct dir_baton *db = dir_baton; 2496 2497 if (db->skip_this) 2498 return SVN_NO_ERROR; 2499 2500 propchange = apr_array_push(db->propchanges); 2501 propchange->name = apr_pstrdup(db->pool, name); 2502 propchange->value = value ? svn_string_dup(value, db->pool) : NULL; 2503 2504 if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind) 2505 SVN_ERR(mark_directory_edited(db, pool)); 2506 2507 return SVN_NO_ERROR; 2508} 2509 2510/* If any of the svn_prop_t objects in PROPCHANGES represents a change 2511 to the SVN_PROP_EXTERNALS property, return that change, else return 2512 null. If PROPCHANGES contains more than one such change, return 2513 the first. */ 2514static const svn_prop_t * 2515externals_prop_changed(const apr_array_header_t *propchanges) 2516{ 2517 int i; 2518 2519 for (i = 0; i < propchanges->nelts; i++) 2520 { 2521 const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t)); 2522 if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0) 2523 return p; 2524 } 2525 2526 return NULL; 2527} 2528 2529 2530 2531/* An svn_delta_editor_t function. */ 2532static svn_error_t * 2533close_directory(void *dir_baton, 2534 apr_pool_t *pool) 2535{ 2536 struct dir_baton *db = dir_baton; 2537 struct edit_baton *eb = db->edit_baton; 2538 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown; 2539 apr_array_header_t *entry_prop_changes; 2540 apr_array_header_t *dav_prop_changes; 2541 apr_array_header_t *regular_prop_changes; 2542 apr_hash_t *base_props; 2543 apr_hash_t *actual_props; 2544 apr_hash_t *new_base_props = NULL; 2545 apr_hash_t *new_actual_props = NULL; 2546 svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM; 2547 apr_time_t new_changed_date = 0; 2548 const char *new_changed_author = NULL; 2549 apr_pool_t *scratch_pool = db->pool; 2550 svn_skel_t *all_work_items = NULL; 2551 svn_skel_t *conflict_skel = NULL; 2552 2553 /* Skip if we're in a conflicted tree. */ 2554 if (db->skip_this) 2555 { 2556 /* Allow the parent to complete its update. */ 2557 SVN_ERR(maybe_release_dir_info(db)); 2558 2559 return SVN_NO_ERROR; 2560 } 2561 2562 if (db->edited) 2563 conflict_skel = db->edit_conflict; 2564 2565 SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes, 2566 &dav_prop_changes, ®ular_prop_changes, pool)); 2567 2568 /* Fetch the existing properties. */ 2569 if ((!db->adding_dir || db->add_existed) 2570 && !db->shadowed) 2571 { 2572 SVN_ERR(svn_wc__get_actual_props(&actual_props, 2573 eb->db, db->local_abspath, 2574 scratch_pool, scratch_pool)); 2575 } 2576 else 2577 actual_props = apr_hash_make(pool); 2578 2579 if (db->add_existed) 2580 { 2581 /* This node already exists. Grab the current pristine properties. */ 2582 SVN_ERR(svn_wc__db_read_pristine_props(&base_props, 2583 eb->db, db->local_abspath, 2584 scratch_pool, scratch_pool)); 2585 } 2586 else if (!db->adding_dir) 2587 { 2588 /* Get the BASE properties for proper merging. */ 2589 SVN_ERR(svn_wc__db_base_get_props(&base_props, 2590 eb->db, db->local_abspath, 2591 scratch_pool, scratch_pool)); 2592 } 2593 else 2594 base_props = apr_hash_make(pool); 2595 2596 /* An incomplete directory might have props which were supposed to be 2597 deleted but weren't. Because the server sent us all the props we're 2598 supposed to have, any previous base props not in this list must be 2599 deleted (issue #1672). */ 2600 if (db->was_incomplete) 2601 { 2602 int i; 2603 apr_hash_t *props_to_delete; 2604 apr_hash_index_t *hi; 2605 2606 /* In a copy of the BASE props, remove every property that we see an 2607 incoming change for. The remaining unmentioned properties are those 2608 which need to be deleted. */ 2609 props_to_delete = apr_hash_copy(pool, base_props); 2610 for (i = 0; i < regular_prop_changes->nelts; i++) 2611 { 2612 const svn_prop_t *prop; 2613 prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t); 2614 svn_hash_sets(props_to_delete, prop->name, NULL); 2615 } 2616 2617 /* Add these props to the incoming propchanges (in 2618 * regular_prop_changes). */ 2619 for (hi = apr_hash_first(pool, props_to_delete); 2620 hi != NULL; 2621 hi = apr_hash_next(hi)) 2622 { 2623 const char *propname = svn__apr_hash_index_key(hi); 2624 svn_prop_t *prop = apr_array_push(regular_prop_changes); 2625 2626 /* Record a deletion for PROPNAME. */ 2627 prop->name = propname; 2628 prop->value = NULL; 2629 } 2630 } 2631 2632 /* If this directory has property changes stored up, now is the time 2633 to deal with them. */ 2634 if (regular_prop_changes->nelts) 2635 { 2636 /* If recording traversal info, then see if the 2637 SVN_PROP_EXTERNALS property on this directory changed, 2638 and record before and after for the change. */ 2639 if (eb->external_func) 2640 { 2641 const svn_prop_t *change 2642 = externals_prop_changed(regular_prop_changes); 2643 2644 if (change) 2645 { 2646 const svn_string_t *new_val_s = change->value; 2647 const svn_string_t *old_val_s; 2648 2649 old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS); 2650 2651 if ((new_val_s == NULL) && (old_val_s == NULL)) 2652 ; /* No value before, no value after... so do nothing. */ 2653 else if (new_val_s && old_val_s 2654 && (svn_string_compare(old_val_s, new_val_s))) 2655 ; /* Value did not change... so do nothing. */ 2656 else if (old_val_s || new_val_s) 2657 /* something changed, record the change */ 2658 { 2659 SVN_ERR((eb->external_func)( 2660 eb->external_baton, 2661 db->local_abspath, 2662 old_val_s, 2663 new_val_s, 2664 db->ambient_depth, 2665 db->pool)); 2666 } 2667 } 2668 } 2669 2670 if (db->shadowed) 2671 { 2672 /* We don't have a relevant actual row, but we need actual properties 2673 to allow property merging without conflicts. */ 2674 if (db->adding_dir) 2675 actual_props = apr_hash_make(scratch_pool); 2676 else 2677 actual_props = base_props; 2678 } 2679 2680 /* Merge pending properties. */ 2681 new_base_props = svn_prop__patch(base_props, regular_prop_changes, 2682 db->pool); 2683 SVN_ERR_W(svn_wc__merge_props(&conflict_skel, 2684 &prop_state, 2685 &new_actual_props, 2686 eb->db, 2687 db->local_abspath, 2688 NULL /* use baseprops */, 2689 base_props, 2690 actual_props, 2691 regular_prop_changes, 2692 db->pool, 2693 scratch_pool), 2694 _("Couldn't do property merge")); 2695 /* After a (not-dry-run) merge, we ALWAYS have props to save. */ 2696 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL); 2697 } 2698 2699 SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date, 2700 &new_changed_author, entry_prop_changes, 2701 scratch_pool, scratch_pool)); 2702 2703 /* Check if we should add some not-present markers before marking the 2704 directory complete (Issue #3569) */ 2705 { 2706 apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath); 2707 2708 if (new_children != NULL) 2709 { 2710 apr_hash_index_t *hi; 2711 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2712 2713 for (hi = apr_hash_first(scratch_pool, new_children); 2714 hi; 2715 hi = apr_hash_next(hi)) 2716 { 2717 const char *child_name; 2718 const char *child_abspath; 2719 const char *child_relpath; 2720 const svn_dirent_t *dirent; 2721 svn_wc__db_status_t status; 2722 svn_node_kind_t child_kind; 2723 svn_error_t *err; 2724 2725 svn_pool_clear(iterpool); 2726 2727 child_name = svn__apr_hash_index_key(hi); 2728 child_abspath = svn_dirent_join(db->local_abspath, child_name, 2729 iterpool); 2730 2731 dirent = svn__apr_hash_index_val(hi); 2732 child_kind = (dirent->kind == svn_node_dir) 2733 ? svn_node_dir 2734 : svn_node_file; 2735 2736 if (db->ambient_depth < svn_depth_immediates 2737 && child_kind == svn_node_dir) 2738 continue; /* We don't need the subdirs */ 2739 2740 /* ### We just check if there is some node in BASE at this path */ 2741 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, 2742 NULL, NULL, NULL, NULL, NULL, NULL, 2743 NULL, NULL, NULL, NULL, NULL, 2744 eb->db, child_abspath, 2745 iterpool, iterpool); 2746 2747 if (!err) 2748 { 2749 svn_boolean_t is_wcroot; 2750 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath, 2751 iterpool)); 2752 2753 if (!is_wcroot) 2754 continue; /* Everything ok... Nothing to do here */ 2755 /* Fall through to allow recovering later */ 2756 } 2757 else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2758 return svn_error_trace(err); 2759 2760 svn_error_clear(err); 2761 2762 child_relpath = svn_relpath_join(db->new_relpath, child_name, 2763 iterpool); 2764 2765 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, 2766 child_abspath, 2767 child_relpath, 2768 eb->repos_root, 2769 eb->repos_uuid, 2770 *eb->target_revision, 2771 child_kind, 2772 NULL, NULL, 2773 iterpool)); 2774 } 2775 2776 svn_pool_destroy(iterpool); 2777 } 2778 } 2779 2780 if (apr_hash_count(db->not_present_files)) 2781 { 2782 apr_hash_index_t *hi; 2783 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2784 2785 /* This should call some new function (which could also be used 2786 for new_children above) to add all the names in single 2787 transaction, but I can't even trigger it. I've tried 2788 ra_local, ra_svn, ra_neon, ra_serf and they all call 2789 close_file before close_dir. */ 2790 for (hi = apr_hash_first(scratch_pool, db->not_present_files); 2791 hi; 2792 hi = apr_hash_next(hi)) 2793 { 2794 const char *child = svn__apr_hash_index_key(hi); 2795 const char *child_abspath, *child_relpath; 2796 2797 svn_pool_clear(iterpool); 2798 2799 child_abspath = svn_dirent_join(db->local_abspath, child, iterpool); 2800 child_relpath = svn_dirent_join(db->new_relpath, child, iterpool); 2801 2802 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, 2803 child_abspath, 2804 child_relpath, 2805 eb->repos_root, 2806 eb->repos_uuid, 2807 *eb->target_revision, 2808 svn_node_file, 2809 NULL, NULL, 2810 iterpool)); 2811 } 2812 svn_pool_destroy(iterpool); 2813 } 2814 2815 /* If this directory is merely an anchor for a targeted child, then we 2816 should not be updating the node at all. */ 2817 if (db->parent_baton == NULL 2818 && *eb->target_basename != '\0') 2819 { 2820 /* And we should not have received any changes! */ 2821 SVN_ERR_ASSERT(db->propchanges->nelts == 0); 2822 /* ... which also implies NEW_CHANGED_* are not set, 2823 and NEW_BASE_PROPS == NULL. */ 2824 } 2825 else 2826 { 2827 apr_hash_t *props; 2828 apr_array_header_t *iprops = NULL; 2829 2830 /* ### we know a base node already exists. it was created in 2831 ### open_directory or add_directory. let's just preserve the 2832 ### existing DEPTH value, and possibly CHANGED_*. */ 2833 /* If we received any changed_* values, then use them. */ 2834 if (SVN_IS_VALID_REVNUM(new_changed_rev)) 2835 db->changed_rev = new_changed_rev; 2836 if (new_changed_date != 0) 2837 db->changed_date = new_changed_date; 2838 if (new_changed_author != NULL) 2839 db->changed_author = new_changed_author; 2840 2841 /* If no depth is set yet, set to infinity. */ 2842 if (db->ambient_depth == svn_depth_unknown) 2843 db->ambient_depth = svn_depth_infinity; 2844 2845 if (eb->depth_is_sticky 2846 && db->ambient_depth != eb->requested_depth) 2847 { 2848 /* After a depth upgrade the entry must reflect the new depth. 2849 Upgrading to infinity changes the depth of *all* directories, 2850 upgrading to something else only changes the target. */ 2851 2852 if (eb->requested_depth == svn_depth_infinity 2853 || (strcmp(db->local_abspath, eb->target_abspath) == 0 2854 && eb->requested_depth > db->ambient_depth)) 2855 { 2856 db->ambient_depth = eb->requested_depth; 2857 } 2858 } 2859 2860 /* Do we have new properties to install? Or shall we simply retain 2861 the prior set of properties? If we're installing new properties, 2862 then we also want to write them to an old-style props file. */ 2863 props = new_base_props; 2864 if (props == NULL) 2865 props = base_props; 2866 2867 if (conflict_skel) 2868 { 2869 svn_skel_t *work_item; 2870 2871 SVN_ERR(complete_conflict(conflict_skel, 2872 db->edit_baton, 2873 db->local_abspath, 2874 db->old_repos_relpath, 2875 db->old_revision, 2876 db->new_relpath, 2877 svn_node_dir, svn_node_dir, 2878 db->pool, scratch_pool)); 2879 2880 SVN_ERR(svn_wc__conflict_create_markers(&work_item, 2881 eb->db, db->local_abspath, 2882 conflict_skel, 2883 scratch_pool, scratch_pool)); 2884 2885 all_work_items = svn_wc__wq_merge(all_work_items, work_item, 2886 scratch_pool); 2887 } 2888 2889 /* Any inherited props to be set set for this base node? */ 2890 if (eb->wcroot_iprops) 2891 { 2892 iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath); 2893 2894 /* close_edit may also update iprops for switched nodes, catching 2895 those for which close_directory is never called (e.g. a switch 2896 with no changes). So as a minor optimization we remove any 2897 iprops from the hash so as not to set them again in 2898 close_edit. */ 2899 if (iprops) 2900 svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL); 2901 } 2902 2903 /* Update the BASE data for the directory and mark the directory 2904 complete */ 2905 SVN_ERR(svn_wc__db_base_add_directory( 2906 eb->db, db->local_abspath, 2907 eb->wcroot_abspath, 2908 db->new_relpath, 2909 eb->repos_root, eb->repos_uuid, 2910 *eb->target_revision, 2911 props, 2912 db->changed_rev, db->changed_date, db->changed_author, 2913 NULL /* children */, 2914 db->ambient_depth, 2915 (dav_prop_changes->nelts > 0) 2916 ? svn_prop_array_to_hash(dav_prop_changes, pool) 2917 : NULL, 2918 conflict_skel, 2919 (! db->shadowed) && new_base_props != NULL, 2920 new_actual_props, 2921 iprops, all_work_items, 2922 scratch_pool)); 2923 } 2924 2925 /* Process all of the queued work items for this directory. */ 2926 SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath, 2927 eb->cancel_func, eb->cancel_baton, 2928 scratch_pool)); 2929 2930 if (conflict_skel && eb->conflict_func) 2931 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, 2932 conflict_skel, 2933 NULL /* merge_options */, 2934 eb->conflict_func, 2935 eb->conflict_baton, 2936 eb->cancel_func, 2937 eb->cancel_baton, 2938 scratch_pool)); 2939 2940 /* Notify of any prop changes on this directory -- but do nothing if 2941 it's an added or skipped directory, because notification has already 2942 happened in that case - unless the add was obstructed by a dir 2943 scheduled for addition without history, in which case we handle 2944 notification here). */ 2945 if (!db->already_notified && eb->notify_func && db->edited) 2946 { 2947 svn_wc_notify_t *notify; 2948 svn_wc_notify_action_t action; 2949 2950 if (db->shadowed || db->edit_obstructed) 2951 action = svn_wc_notify_update_shadowed_update; 2952 else if (db->obstruction_found || db->add_existed) 2953 action = svn_wc_notify_exists; 2954 else 2955 action = svn_wc_notify_update_update; 2956 2957 notify = svn_wc_create_notify(db->local_abspath, action, pool); 2958 notify->kind = svn_node_dir; 2959 notify->prop_state = prop_state; 2960 notify->revision = *eb->target_revision; 2961 notify->old_revision = db->old_revision; 2962 2963 eb->notify_func(eb->notify_baton, notify, scratch_pool); 2964 } 2965 2966 /* We're done with this directory, so remove one reference from the 2967 bump information. */ 2968 SVN_ERR(maybe_release_dir_info(db)); 2969 2970 return SVN_NO_ERROR; 2971} 2972 2973 2974/* Common code for 'absent_file' and 'absent_directory'. */ 2975static svn_error_t * 2976absent_node(const char *path, 2977 svn_node_kind_t absent_kind, 2978 void *parent_baton, 2979 apr_pool_t *pool) 2980{ 2981 struct dir_baton *pb = parent_baton; 2982 struct edit_baton *eb = pb->edit_baton; 2983 apr_pool_t *scratch_pool = svn_pool_create(pool); 2984 const char *name = svn_dirent_basename(path, NULL); 2985 const char *local_abspath; 2986 svn_error_t *err; 2987 svn_wc__db_status_t status; 2988 svn_node_kind_t kind; 2989 2990 if (pb->skip_this) 2991 return SVN_NO_ERROR; 2992 2993 SVN_ERR(mark_directory_edited(pb, scratch_pool)); 2994 2995 local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool); 2996 2997 /* If an item by this name is scheduled for addition that's a 2998 genuine tree-conflict. */ 2999 err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 3000 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3001 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3002 NULL, NULL, NULL, NULL, 3003 eb->db, local_abspath, 3004 scratch_pool, scratch_pool); 3005 3006 if (err) 3007 { 3008 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 3009 return svn_error_trace(err); 3010 3011 svn_error_clear(err); 3012 status = svn_wc__db_status_not_present; 3013 kind = svn_node_unknown; 3014 } 3015 3016 if (status == svn_wc__db_status_normal) 3017 { 3018 svn_boolean_t wcroot; 3019 /* We found an obstructing working copy or a file external! */ 3020 3021 SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath, 3022 scratch_pool)); 3023 3024 if (wcroot) 3025 { 3026 /* 3027 We have an obstructing working copy; possibly a directory external 3028 3029 We can do two things now: 3030 1) notify the user, record a skip, etc. 3031 2) Just record the absent node in BASE in the parent 3032 working copy. 3033 3034 As option 2 happens to be exactly what we do anyway, fall through. 3035 */ 3036 } 3037 else 3038 { 3039 /* The server asks us to replace a file external 3040 (Existing BASE node; not reported by the working copy crawler or 3041 there would have been a delete_entry() call. 3042 3043 There is no way we can store this state in the working copy as 3044 the BASE layer is already filled. 3045 3046 We could error out, but that is not helping anybody; the user is not 3047 even seeing with what the file external would be replaced, so let's 3048 report a skip and continue the update. 3049 */ 3050 3051 if (eb->notify_func) 3052 { 3053 svn_wc_notify_t *notify; 3054 notify = svn_wc_create_notify( 3055 local_abspath, 3056 svn_wc_notify_update_skip_obstruction, 3057 scratch_pool); 3058 3059 eb->notify_func(eb->notify_baton, notify, scratch_pool); 3060 } 3061 3062 svn_pool_destroy(scratch_pool); 3063 return SVN_NO_ERROR; 3064 } 3065 } 3066 else if (status == svn_wc__db_status_not_present 3067 || status == svn_wc__db_status_server_excluded 3068 || status == svn_wc__db_status_excluded) 3069 { 3070 /* The BASE node is not actually there, so we can safely turn it into 3071 an absent node */ 3072 } 3073 else 3074 { 3075 /* We have a local addition. If this would be a BASE node it would have 3076 been deleted before we get here. (Which might have turned it into 3077 a copy). 3078 3079 ### This should be recorded as a tree conflict and the update 3080 ### can just continue, as we can just record the absent status 3081 ### in BASE. 3082 */ 3083 SVN_ERR_ASSERT(status != svn_wc__db_status_normal); 3084 3085 return svn_error_createf( 3086 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 3087 _("Failed to mark '%s' absent: item of the same name is already " 3088 "scheduled for addition"), 3089 svn_dirent_local_style(local_abspath, pool)); 3090 } 3091 3092 { 3093 const char *repos_relpath; 3094 repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool); 3095 3096 /* Insert an excluded node below the parent node to note that this child 3097 is absent. (This puts it in the parent db if the child is obstructed) */ 3098 SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath, 3099 repos_relpath, eb->repos_root, 3100 eb->repos_uuid, 3101 *(eb->target_revision), 3102 absent_kind, 3103 svn_wc__db_status_server_excluded, 3104 NULL, NULL, 3105 scratch_pool)); 3106 } 3107 3108 svn_pool_destroy(scratch_pool); 3109 3110 return SVN_NO_ERROR; 3111} 3112 3113 3114/* An svn_delta_editor_t function. */ 3115static svn_error_t * 3116absent_file(const char *path, 3117 void *parent_baton, 3118 apr_pool_t *pool) 3119{ 3120 return absent_node(path, svn_node_file, parent_baton, pool); 3121} 3122 3123 3124/* An svn_delta_editor_t function. */ 3125static svn_error_t * 3126absent_directory(const char *path, 3127 void *parent_baton, 3128 apr_pool_t *pool) 3129{ 3130 return absent_node(path, svn_node_dir, parent_baton, pool); 3131} 3132 3133 3134/* An svn_delta_editor_t function. */ 3135static svn_error_t * 3136add_file(const char *path, 3137 void *parent_baton, 3138 const char *copyfrom_path, 3139 svn_revnum_t copyfrom_rev, 3140 apr_pool_t *pool, 3141 void **file_baton) 3142{ 3143 struct dir_baton *pb = parent_baton; 3144 struct edit_baton *eb = pb->edit_baton; 3145 struct file_baton *fb; 3146 svn_node_kind_t kind = svn_node_none; 3147 svn_node_kind_t wc_kind = svn_node_unknown; 3148 svn_wc__db_status_t status = svn_wc__db_status_normal; 3149 apr_pool_t *scratch_pool; 3150 svn_boolean_t conflicted = FALSE; 3151 svn_boolean_t conflict_ignored = FALSE; 3152 svn_boolean_t versioned_locally_and_present = FALSE; 3153 svn_skel_t *tree_conflict = NULL; 3154 svn_error_t *err = SVN_NO_ERROR; 3155 3156 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev))); 3157 3158 SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool)); 3159 *file_baton = fb; 3160 3161 if (fb->skip_this) 3162 return SVN_NO_ERROR; 3163 3164 SVN_ERR(mark_file_edited(fb, pool)); 3165 3166 /* The file_pool can stick around for a *long* time, so we want to 3167 use a subpool for any temporary allocations. */ 3168 scratch_pool = svn_pool_create(pool); 3169 3170 3171 /* It may not be named the same as the administrative directory. */ 3172 if (svn_wc_is_adm_dir(fb->name, pool)) 3173 return svn_error_createf( 3174 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 3175 _("Failed to add file '%s': object of the same name as the " 3176 "administrative directory"), 3177 svn_dirent_local_style(fb->local_abspath, pool)); 3178 3179 if (!eb->clean_checkout) 3180 { 3181 SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool)); 3182 3183 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, 3184 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3185 NULL, NULL, NULL, NULL, NULL, 3186 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, 3187 eb->db, fb->local_abspath, 3188 scratch_pool, scratch_pool); 3189 } 3190 3191 if (err) 3192 { 3193 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 3194 return svn_error_trace(err); 3195 3196 svn_error_clear(err); 3197 wc_kind = svn_node_unknown; 3198 conflicted = FALSE; 3199 3200 versioned_locally_and_present = FALSE; 3201 } 3202 else if (wc_kind == svn_node_dir 3203 && status == svn_wc__db_status_normal) 3204 { 3205 /* !! We found the root of a separate working copy obstructing the wc !! 3206 3207 If the directory would be part of our own working copy then 3208 we wouldn't have been called as an add_file(). 3209 3210 The only thing we can do is add a not-present node, to allow 3211 a future update to bring in the new files when the problem is 3212 resolved. */ 3213 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), 3214 (void *)1); 3215 3216 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3217 fb->skip_this = TRUE; 3218 fb->already_notified = TRUE; 3219 3220 do_notification(eb, fb->local_abspath, svn_node_file, 3221 svn_wc_notify_update_skip_obstruction, scratch_pool); 3222 3223 svn_pool_destroy(scratch_pool); 3224 3225 return SVN_NO_ERROR; 3226 } 3227 else if (status == svn_wc__db_status_normal 3228 && (wc_kind == svn_node_file 3229 || wc_kind == svn_node_symlink)) 3230 { 3231 /* We found a file external occupating the place we need in BASE. 3232 3233 We can't add a not-present node in this case as that would overwrite 3234 the file external. Luckily the file external itself stops us from 3235 forgetting a child of this parent directory like an obstructing 3236 working copy would. 3237 3238 The reason we get here is that the adm crawler doesn't report 3239 file externals. 3240 */ 3241 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3242 fb->skip_this = TRUE; 3243 fb->already_notified = TRUE; 3244 3245 do_notification(eb, fb->local_abspath, svn_node_file, 3246 svn_wc_notify_update_skip_obstruction, scratch_pool); 3247 3248 svn_pool_destroy(scratch_pool); 3249 3250 return SVN_NO_ERROR; 3251 } 3252 else if (wc_kind == svn_node_unknown) 3253 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ 3254 else 3255 versioned_locally_and_present = IS_NODE_PRESENT(status); 3256 3257 3258 /* Is this path a conflict victim? */ 3259 if (fb->shadowed) 3260 conflicted = FALSE; /* Conflict applies to WORKING */ 3261 else if (conflicted) 3262 { 3263 if (pb->deletion_conflicts) 3264 tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name); 3265 3266 if (tree_conflict) 3267 { 3268 svn_wc_conflict_reason_t reason; 3269 /* So this deletion wasn't just a deletion, it is actually a 3270 replacement. Let's install a better tree conflict. */ 3271 3272 /* ### Should store the conflict in DB to allow reinstalling 3273 ### with theoretically more data in close_directory() */ 3274 3275 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 3276 eb->db, 3277 fb->local_abspath, 3278 tree_conflict, 3279 fb->pool, fb->pool)); 3280 3281 tree_conflict = svn_wc__conflict_skel_create(fb->pool); 3282 3283 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3284 tree_conflict, 3285 eb->db, fb->local_abspath, 3286 reason, svn_wc_conflict_action_replace, 3287 NULL, 3288 fb->pool, fb->pool)); 3289 3290 /* And now stop checking for conflicts here and just perform 3291 a shadowed update */ 3292 fb->edit_conflict = tree_conflict; /* Cache for close_file */ 3293 tree_conflict = NULL; /* No direct notification */ 3294 fb->shadowed = TRUE; /* Just continue */ 3295 conflicted = FALSE; /* No skip */ 3296 } 3297 else 3298 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 3299 eb->db, fb->local_abspath, pool)); 3300 } 3301 3302 /* Now the usual conflict handling: skip. */ 3303 if (conflicted) 3304 { 3305 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3306 3307 fb->skip_this = TRUE; 3308 fb->already_notified = TRUE; 3309 3310 /* We skip this node, but once the update completes the parent node will 3311 be updated to the new revision. So a future recursive update of the 3312 parent will not bring in this new node as the revision of the parent 3313 describes to the repository that all children are available. 3314 3315 To resolve this problem, we add a not-present node to allow bringing 3316 the node in once this conflict is resolved. 3317 3318 Note that we can safely assume that no present base node exists, 3319 because then we would not have received an add_file. 3320 */ 3321 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), 3322 (void *)1); 3323 3324 do_notification(eb, fb->local_abspath, svn_node_unknown, 3325 svn_wc_notify_skip_conflicted, scratch_pool); 3326 3327 svn_pool_destroy(scratch_pool); 3328 3329 return SVN_NO_ERROR; 3330 } 3331 else if (conflict_ignored) 3332 { 3333 fb->shadowed = TRUE; 3334 } 3335 3336 if (fb->shadowed) 3337 { 3338 /* Nothing to check; does not and will not exist in working copy */ 3339 } 3340 else if (versioned_locally_and_present) 3341 { 3342 /* What to do with a versioned or schedule-add file: 3343 3344 If the UUID doesn't match the parent's, or the URL isn't a child of 3345 the parent dir's URL, it's an error. 3346 3347 Set add_existed so that user notification is delayed until after any 3348 text or prop conflicts have been found. 3349 3350 Whether the incoming add is a symlink or a file will only be known in 3351 close_file(), when the props are known. So with a locally added file 3352 or symlink, let close_file() check for a tree conflict. 3353 3354 We will never see missing files here, because these would be 3355 re-added during the crawler phase. */ 3356 svn_boolean_t local_is_file; 3357 3358 /* Is the local node a copy or move */ 3359 if (status == svn_wc__db_status_added) 3360 SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL, 3361 NULL, NULL, NULL, 3362 eb->db, fb->local_abspath, 3363 scratch_pool, scratch_pool)); 3364 3365 /* Is there something that is a file? */ 3366 local_is_file = (wc_kind == svn_node_file 3367 || wc_kind == svn_node_symlink); 3368 3369 /* Do tree conflict checking if 3370 * - if there is a local copy. 3371 * - if this is a switch operation 3372 * - the node kinds mismatch 3373 * 3374 * During switch, local adds at the same path as incoming adds get 3375 * "lost" in that switching back to the original will no longer have the 3376 * local add. So switch always alerts the user with a tree conflict. */ 3377 if (!eb->adds_as_modification 3378 || !local_is_file 3379 || status != svn_wc__db_status_added) 3380 { 3381 SVN_ERR(check_tree_conflict(&tree_conflict, eb, 3382 fb->local_abspath, 3383 status, FALSE, svn_node_none, 3384 svn_wc_conflict_action_add, 3385 scratch_pool, scratch_pool)); 3386 } 3387 3388 if (tree_conflict == NULL) 3389 fb->add_existed = TRUE; /* Take over WORKING */ 3390 else 3391 fb->shadowed = TRUE; /* Only update BASE */ 3392 3393 } 3394 else if (kind != svn_node_none) 3395 { 3396 /* There's an unversioned node at this path. */ 3397 fb->obstruction_found = TRUE; 3398 3399 /* Unversioned, obstructing files are handled by text merge/conflict, 3400 * if unversioned obstructions are allowed. */ 3401 if (! (kind == svn_node_file && eb->allow_unver_obstructions)) 3402 { 3403 /* Bring in the node as deleted */ /* ### Obstructed Conflict */ 3404 fb->shadowed = TRUE; 3405 3406 /* Mark a conflict */ 3407 tree_conflict = svn_wc__conflict_skel_create(fb->pool); 3408 3409 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3410 tree_conflict, 3411 eb->db, fb->local_abspath, 3412 svn_wc_conflict_reason_unversioned, 3413 svn_wc_conflict_action_add, 3414 NULL, 3415 fb->pool, scratch_pool)); 3416 } 3417 } 3418 3419 /* When this is not the update target add a not-present BASE node now, 3420 to allow marking the parent directory complete in its close_edit() call. 3421 This resolves issues when that occurs before the close_file(). */ 3422 if (pb->parent_baton 3423 || *eb->target_basename == '\0' 3424 || (strcmp(fb->local_abspath, eb->target_abspath) != 0)) 3425 { 3426 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), 3427 (void *)1); 3428 } 3429 3430 if (tree_conflict != NULL) 3431 { 3432 SVN_ERR(complete_conflict(tree_conflict, 3433 fb->edit_baton, 3434 fb->local_abspath, 3435 fb->old_repos_relpath, 3436 fb->old_revision, 3437 fb->new_relpath, 3438 wc_kind, 3439 svn_node_file, 3440 fb->pool, scratch_pool)); 3441 3442 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, 3443 fb->local_abspath, 3444 tree_conflict, NULL, 3445 scratch_pool)); 3446 3447 if (eb->conflict_func) 3448 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, 3449 tree_conflict, 3450 NULL /* merge_options */, 3451 eb->conflict_func, 3452 eb->conflict_baton, 3453 eb->cancel_func, 3454 eb->cancel_baton, 3455 scratch_pool)); 3456 3457 fb->already_notified = TRUE; 3458 do_notification(eb, fb->local_abspath, svn_node_file, 3459 svn_wc_notify_tree_conflict, scratch_pool); 3460 } 3461 3462 svn_pool_destroy(scratch_pool); 3463 3464 return SVN_NO_ERROR; 3465} 3466 3467 3468/* An svn_delta_editor_t function. */ 3469static svn_error_t * 3470open_file(const char *path, 3471 void *parent_baton, 3472 svn_revnum_t base_revision, 3473 apr_pool_t *pool, 3474 void **file_baton) 3475{ 3476 struct dir_baton *pb = parent_baton; 3477 struct edit_baton *eb = pb->edit_baton; 3478 struct file_baton *fb; 3479 svn_boolean_t conflicted; 3480 svn_boolean_t conflict_ignored = FALSE; 3481 svn_boolean_t have_work; 3482 svn_wc__db_status_t status; 3483 svn_node_kind_t wc_kind; 3484 svn_skel_t *tree_conflict = NULL; 3485 3486 /* the file_pool can stick around for a *long* time, so we want to use 3487 a subpool for any temporary allocations. */ 3488 apr_pool_t *scratch_pool = svn_pool_create(pool); 3489 3490 SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool)); 3491 *file_baton = fb; 3492 3493 if (fb->skip_this) 3494 return SVN_NO_ERROR; 3495 3496 /* Detect obstructing working copies */ 3497 { 3498 svn_boolean_t is_root; 3499 3500 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath, 3501 pool)); 3502 3503 if (is_root) 3504 { 3505 /* Just skip this node; a future update will handle it */ 3506 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3507 fb->skip_this = TRUE; 3508 fb->already_notified = TRUE; 3509 3510 do_notification(eb, fb->local_abspath, svn_node_file, 3511 svn_wc_notify_update_skip_obstruction, pool); 3512 3513 return SVN_NO_ERROR; 3514 } 3515 } 3516 3517 /* Sanity check. */ 3518 3519 /* If replacing, make sure the .svn entry already exists. */ 3520 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision, 3521 &fb->old_repos_relpath, NULL, NULL, 3522 &fb->changed_rev, &fb->changed_date, 3523 &fb->changed_author, NULL, 3524 &fb->original_checksum, NULL, NULL, NULL, 3525 NULL, NULL, NULL, NULL, NULL, NULL, 3526 &conflicted, NULL, NULL, &fb->local_prop_mods, 3527 NULL, NULL, &have_work, 3528 eb->db, fb->local_abspath, 3529 fb->pool, scratch_pool)); 3530 3531 if (have_work) 3532 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision, 3533 &fb->old_repos_relpath, NULL, NULL, 3534 &fb->changed_rev, &fb->changed_date, 3535 &fb->changed_author, NULL, 3536 &fb->original_checksum, NULL, NULL, 3537 NULL, NULL, NULL, 3538 eb->db, fb->local_abspath, 3539 fb->pool, scratch_pool)); 3540 3541 /* Is this path a conflict victim? */ 3542 if (fb->shadowed) 3543 conflicted = FALSE; /* Conflict applies to WORKING */ 3544 else if (conflicted) 3545 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 3546 eb->db, fb->local_abspath, pool)); 3547 if (conflicted) 3548 { 3549 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3550 3551 fb->skip_this = TRUE; 3552 fb->already_notified = TRUE; 3553 3554 do_notification(eb, fb->local_abspath, svn_node_unknown, 3555 svn_wc_notify_skip_conflicted, scratch_pool); 3556 3557 svn_pool_destroy(scratch_pool); 3558 3559 return SVN_NO_ERROR; 3560 } 3561 else if (conflict_ignored) 3562 { 3563 fb->shadowed = TRUE; 3564 } 3565 3566 /* Check for conflicts only when we haven't already recorded 3567 * a tree-conflict on a parent node. */ 3568 if (!fb->shadowed) 3569 SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath, 3570 status, TRUE, svn_node_file, 3571 svn_wc_conflict_action_edit, 3572 fb->pool, scratch_pool)); 3573 3574 /* Is this path the victim of a newly-discovered tree conflict? */ 3575 if (tree_conflict != NULL) 3576 { 3577 svn_wc_conflict_reason_t reason; 3578 fb->edit_conflict = tree_conflict; 3579 /* Other modifications wouldn't be a tree conflict */ 3580 3581 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 3582 eb->db, fb->local_abspath, 3583 tree_conflict, 3584 scratch_pool, scratch_pool)); 3585 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted 3586 || reason == svn_wc_conflict_reason_moved_away 3587 || reason == svn_wc_conflict_reason_replaced 3588 || reason == svn_wc_conflict_reason_obstructed); 3589 3590 /* Continue updating BASE */ 3591 if (reason == svn_wc_conflict_reason_obstructed) 3592 fb->edit_obstructed = TRUE; 3593 else 3594 fb->shadowed = TRUE; 3595 } 3596 3597 svn_pool_destroy(scratch_pool); 3598 3599 return SVN_NO_ERROR; 3600} 3601 3602/* Implements svn_stream_lazyopen_func_t. */ 3603static svn_error_t * 3604lazy_open_source(svn_stream_t **stream, 3605 void *baton, 3606 apr_pool_t *result_pool, 3607 apr_pool_t *scratch_pool) 3608{ 3609 struct file_baton *fb = baton; 3610 3611 SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db, 3612 fb->local_abspath, 3613 fb->original_checksum, 3614 result_pool, scratch_pool)); 3615 3616 3617 return SVN_NO_ERROR; 3618} 3619 3620struct lazy_target_baton { 3621 struct file_baton *fb; 3622 struct handler_baton *hb; 3623 struct edit_baton *eb; 3624}; 3625 3626/* Implements svn_stream_lazyopen_func_t. */ 3627static svn_error_t * 3628lazy_open_target(svn_stream_t **stream, 3629 void *baton, 3630 apr_pool_t *result_pool, 3631 apr_pool_t *scratch_pool) 3632{ 3633 struct lazy_target_baton *tb = baton; 3634 3635 SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath, 3636 NULL, &tb->hb->new_text_base_sha1_checksum, 3637 tb->fb->edit_baton->db, 3638 tb->eb->wcroot_abspath, 3639 result_pool, scratch_pool)); 3640 3641 return SVN_NO_ERROR; 3642} 3643 3644/* An svn_delta_editor_t function. */ 3645static svn_error_t * 3646apply_textdelta(void *file_baton, 3647 const char *expected_checksum, 3648 apr_pool_t *pool, 3649 svn_txdelta_window_handler_t *handler, 3650 void **handler_baton) 3651{ 3652 struct file_baton *fb = file_baton; 3653 apr_pool_t *handler_pool = svn_pool_create(fb->pool); 3654 struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb)); 3655 struct edit_baton *eb = fb->edit_baton; 3656 const svn_checksum_t *recorded_base_checksum; 3657 svn_checksum_t *expected_base_checksum; 3658 svn_stream_t *source; 3659 struct lazy_target_baton *tb; 3660 svn_stream_t *target; 3661 3662 if (fb->skip_this) 3663 { 3664 *handler = svn_delta_noop_window_handler; 3665 *handler_baton = NULL; 3666 return SVN_NO_ERROR; 3667 } 3668 3669 SVN_ERR(mark_file_edited(fb, pool)); 3670 3671 /* Parse checksum or sets expected_base_checksum to NULL */ 3672 SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5, 3673 expected_checksum, pool)); 3674 3675 /* Before applying incoming svndiff data to text base, make sure 3676 text base hasn't been corrupted, and that its checksum 3677 matches the expected base checksum. */ 3678 3679 /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and 3680 check our RECORDED_BASE_CHECKSUM. (In WC-1, we could not do this test 3681 for replaced nodes because we didn't store the checksum of the "revert 3682 base". In WC-NG, we do and we can.) */ 3683 recorded_base_checksum = fb->original_checksum; 3684 3685 /* If we have a checksum that we want to compare to a MD5 checksum, 3686 ensure that it is a MD5 checksum */ 3687 if (recorded_base_checksum 3688 && expected_base_checksum 3689 && recorded_base_checksum->kind != svn_checksum_md5) 3690 SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum, 3691 eb->db, eb->wcroot_abspath, 3692 recorded_base_checksum, pool, pool)); 3693 3694 3695 if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum)) 3696 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL, 3697 _("Checksum mismatch for '%s':\n" 3698 " expected: %s\n" 3699 " recorded: %s\n"), 3700 svn_dirent_local_style(fb->local_abspath, pool), 3701 svn_checksum_to_cstring_display(expected_base_checksum, 3702 pool), 3703 svn_checksum_to_cstring_display(recorded_base_checksum, 3704 pool)); 3705 3706 /* Open the text base for reading, unless this is an added file. */ 3707 3708 /* 3709 kff todo: what we really need to do here is: 3710 3711 1. See if there's a file or dir by this name already here. 3712 2. See if it's under revision control. 3713 3. If both are true, open text-base. 3714 4. If only 1 is true, bail, because we can't go destroying user's 3715 files (or as an alternative to bailing, move it to some tmp 3716 name and somehow tell the user, but communicating with the 3717 user without erroring is a whole callback system we haven't 3718 finished inventing yet.) 3719 */ 3720 3721 if (! fb->adding_file) 3722 { 3723 SVN_ERR_ASSERT(!fb->original_checksum 3724 || fb->original_checksum->kind == svn_checksum_sha1); 3725 3726 source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE, 3727 handler_pool); 3728 } 3729 else 3730 { 3731 source = svn_stream_empty(handler_pool); 3732 } 3733 3734 /* If we don't have a recorded checksum, use the ra provided checksum */ 3735 if (!recorded_base_checksum) 3736 recorded_base_checksum = expected_base_checksum; 3737 3738 /* Checksum the text base while applying deltas */ 3739 if (recorded_base_checksum) 3740 { 3741 hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum, 3742 handler_pool); 3743 3744 /* Wrap stream and store reference to allow calculating the 3745 checksum. */ 3746 source = svn_stream_checksummed2(source, 3747 &hb->actual_source_checksum, 3748 NULL, recorded_base_checksum->kind, 3749 TRUE, handler_pool); 3750 hb->source_checksum_stream = source; 3751 } 3752 3753 tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton)); 3754 tb->hb = hb; 3755 tb->fb = fb; 3756 tb->eb = eb; 3757 target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool); 3758 3759 /* Prepare to apply the delta. */ 3760 svn_txdelta_apply(source, target, 3761 hb->new_text_base_md5_digest, 3762 hb->new_text_base_tmp_abspath /* error_info */, 3763 handler_pool, 3764 &hb->apply_handler, &hb->apply_baton); 3765 3766 hb->pool = handler_pool; 3767 hb->fb = fb; 3768 3769 /* We're all set. */ 3770 *handler_baton = hb; 3771 *handler = window_handler; 3772 3773 return SVN_NO_ERROR; 3774} 3775 3776 3777/* An svn_delta_editor_t function. */ 3778static svn_error_t * 3779change_file_prop(void *file_baton, 3780 const char *name, 3781 const svn_string_t *value, 3782 apr_pool_t *scratch_pool) 3783{ 3784 struct file_baton *fb = file_baton; 3785 svn_prop_t *propchange; 3786 3787 if (fb->skip_this) 3788 return SVN_NO_ERROR; 3789 3790 /* Push a new propchange to the file baton's array of propchanges */ 3791 propchange = apr_array_push(fb->propchanges); 3792 propchange->name = apr_pstrdup(fb->pool, name); 3793 propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; 3794 3795 if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind) 3796 SVN_ERR(mark_file_edited(fb, scratch_pool)); 3797 3798 if (! fb->shadowed 3799 && strcmp(name, SVN_PROP_SPECIAL) == 0) 3800 { 3801 struct edit_baton *eb = fb->edit_baton; 3802 svn_boolean_t modified = FALSE; 3803 svn_boolean_t becomes_symlink; 3804 svn_boolean_t was_symlink; 3805 3806 /* Let's see if we have a change as in some scenarios servers report 3807 non-changes of properties. */ 3808 becomes_symlink = (value != NULL); 3809 3810 if (fb->adding_file) 3811 was_symlink = becomes_symlink; /* No change */ 3812 else 3813 { 3814 apr_hash_t *props; 3815 3816 /* We read the server-props, not the ACTUAL props here as we just 3817 want to see if this is really an incoming prop change. */ 3818 SVN_ERR(svn_wc__db_base_get_props(&props, eb->db, 3819 fb->local_abspath, 3820 scratch_pool, scratch_pool)); 3821 3822 was_symlink = ((props 3823 && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL) 3824 ? svn_tristate_true 3825 : svn_tristate_false); 3826 } 3827 3828 if (was_symlink != becomes_symlink) 3829 { 3830 /* If the local node was not modified, we continue as usual, if 3831 modified we want a tree conflict just like how we would handle 3832 it when receiving a delete + add (aka "replace") */ 3833 if (fb->local_prop_mods) 3834 modified = TRUE; 3835 else 3836 SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db, 3837 fb->local_abspath, 3838 FALSE, scratch_pool)); 3839 } 3840 3841 if (modified) 3842 { 3843 if (!fb->edit_conflict) 3844 fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool); 3845 3846 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3847 fb->edit_conflict, 3848 eb->db, fb->local_abspath, 3849 svn_wc_conflict_reason_edited, 3850 svn_wc_conflict_action_replace, 3851 NULL, 3852 fb->pool, scratch_pool)); 3853 3854 SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, 3855 fb->local_abspath, fb->old_repos_relpath, 3856 fb->old_revision, fb->new_relpath, 3857 svn_node_file, svn_node_file, 3858 fb->pool, scratch_pool)); 3859 3860 /* Create a copy of the existing (pre update) BASE node in WORKING, 3861 mark a tree conflict and handle the rest of the update as 3862 shadowed */ 3863 SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath, 3864 fb->edit_conflict, NULL, 3865 scratch_pool)); 3866 3867 do_notification(eb, fb->local_abspath, svn_node_file, 3868 svn_wc_notify_tree_conflict, scratch_pool); 3869 3870 /* Ok, we introduced a replacement, so we can now handle the rest 3871 as a normal shadowed update */ 3872 fb->shadowed = TRUE; 3873 fb->add_existed = FALSE; 3874 fb->already_notified = TRUE; 3875 } 3876 } 3877 3878 return SVN_NO_ERROR; 3879} 3880 3881/* Perform the actual merge of file changes between an original file, 3882 identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file 3883 identified by NEW_CHECKSUM. 3884 3885 Merge the result into LOCAL_ABSPATH, which is part of the working copy 3886 identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming 3887 the intermediate files. 3888 3889 The rest of the arguments are passed to svn_wc__internal_merge(). 3890 */ 3891svn_error_t * 3892svn_wc__perform_file_merge(svn_skel_t **work_items, 3893 svn_skel_t **conflict_skel, 3894 svn_boolean_t *found_conflict, 3895 svn_wc__db_t *db, 3896 const char *local_abspath, 3897 const char *wri_abspath, 3898 const svn_checksum_t *new_checksum, 3899 const svn_checksum_t *original_checksum, 3900 apr_hash_t *old_actual_props, 3901 const apr_array_header_t *ext_patterns, 3902 svn_revnum_t old_revision, 3903 svn_revnum_t target_revision, 3904 const apr_array_header_t *propchanges, 3905 const char *diff3_cmd, 3906 svn_cancel_func_t cancel_func, 3907 void *cancel_baton, 3908 apr_pool_t *result_pool, 3909 apr_pool_t *scratch_pool) 3910{ 3911 /* Actual file exists and has local mods: 3912 Now we need to let loose svn_wc__internal_merge() to merge 3913 the textual changes into the working file. */ 3914 const char *oldrev_str, *newrev_str, *mine_str; 3915 const char *merge_left; 3916 svn_boolean_t delete_left = FALSE; 3917 const char *path_ext = ""; 3918 const char *new_text_base_tmp_abspath; 3919 enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged; 3920 svn_skel_t *work_item; 3921 3922 *work_items = NULL; 3923 3924 SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath, 3925 db, wri_abspath, new_checksum, 3926 scratch_pool, scratch_pool)); 3927 3928 /* If we have any file extensions we're supposed to 3929 preserve in generated conflict file names, then find 3930 this path's extension. But then, if it isn't one of 3931 the ones we want to keep in conflict filenames, 3932 pretend it doesn't have an extension at all. */ 3933 if (ext_patterns && ext_patterns->nelts) 3934 { 3935 svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool); 3936 if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns))) 3937 path_ext = ""; 3938 } 3939 3940 /* old_revision can be invalid when the conflict is against a 3941 local addition */ 3942 if (!SVN_IS_VALID_REVNUM(old_revision)) 3943 old_revision = 0; 3944 3945 oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s", 3946 old_revision, 3947 *path_ext ? "." : "", 3948 *path_ext ? path_ext : ""); 3949 3950 newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s", 3951 target_revision, 3952 *path_ext ? "." : "", 3953 *path_ext ? path_ext : ""); 3954 mine_str = apr_psprintf(scratch_pool, ".mine%s%s", 3955 *path_ext ? "." : "", 3956 *path_ext ? path_ext : ""); 3957 3958 if (! original_checksum) 3959 { 3960 SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath, 3961 result_pool, scratch_pool)); 3962 delete_left = TRUE; 3963 } 3964 else 3965 SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath, 3966 original_checksum, 3967 result_pool, scratch_pool)); 3968 3969 /* Merge the changes from the old textbase to the new 3970 textbase into the file we're updating. 3971 Remember that this function wants full paths! */ 3972 SVN_ERR(svn_wc__internal_merge(&work_item, 3973 conflict_skel, 3974 &merge_outcome, 3975 db, 3976 merge_left, 3977 new_text_base_tmp_abspath, 3978 local_abspath, 3979 wri_abspath, 3980 oldrev_str, newrev_str, mine_str, 3981 old_actual_props, 3982 FALSE /* dry_run */, 3983 diff3_cmd, NULL, propchanges, 3984 cancel_func, cancel_baton, 3985 result_pool, scratch_pool)); 3986 3987 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 3988 *found_conflict = (merge_outcome == svn_wc_merge_conflict); 3989 3990 /* If we created a temporary left merge file, get rid of it. */ 3991 if (delete_left) 3992 { 3993 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath, 3994 merge_left, 3995 result_pool, scratch_pool)); 3996 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 3997 } 3998 3999 return SVN_NO_ERROR; 4000} 4001 4002/* This is the small planet. It has the complex responsibility of 4003 * "integrating" a new revision of a file into a working copy. 4004 * 4005 * Given a file_baton FB for a file either already under version control, or 4006 * prepared (see below) to join version control, fully install a 4007 * new revision of the file. 4008 * 4009 * ### transitional: installation of the working file will be handled 4010 * ### by the *INSTALL_PRISTINE flag. 4011 * 4012 * By "install", we mean: create a new text-base and prop-base, merge 4013 * any textual and property changes into the working file, and finally 4014 * update all metadata so that the working copy believes it has a new 4015 * working revision of the file. All of this work includes being 4016 * sensitive to eol translation, keyword substitution, and performing 4017 * all actions accumulated the parent directory's work queue. 4018 * 4019 * Set *CONTENT_STATE to the state of the contents after the 4020 * installation. 4021 * 4022 * Return values are allocated in RESULT_POOL and temporary allocations 4023 * are performed in SCRATCH_POOL. 4024 */ 4025static svn_error_t * 4026merge_file(svn_skel_t **work_items, 4027 svn_skel_t **conflict_skel, 4028 svn_boolean_t *install_pristine, 4029 const char **install_from, 4030 svn_wc_notify_state_t *content_state, 4031 struct file_baton *fb, 4032 apr_hash_t *actual_props, 4033 apr_time_t last_changed_date, 4034 apr_pool_t *result_pool, 4035 apr_pool_t *scratch_pool) 4036{ 4037 struct edit_baton *eb = fb->edit_baton; 4038 struct dir_baton *pb = fb->dir_baton; 4039 svn_boolean_t is_locally_modified; 4040 svn_boolean_t found_text_conflict = FALSE; 4041 4042 SVN_ERR_ASSERT(! fb->shadowed 4043 && ! fb->obstruction_found 4044 && ! fb->edit_obstructed); 4045 4046 /* 4047 When this function is called on file F, we assume the following 4048 things are true: 4049 4050 - The new pristine text of F is present in the pristine store 4051 iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL. 4052 4053 - The WC metadata still reflects the old version of F. 4054 (We can still access the old pristine base text of F.) 4055 4056 The goal is to update the local working copy of F to reflect 4057 the changes received from the repository, preserving any local 4058 modifications. 4059 */ 4060 4061 *work_items = NULL; 4062 *install_pristine = FALSE; 4063 *install_from = NULL; 4064 4065 /* Start by splitting the file path, getting an access baton for the parent, 4066 and an entry for the file if any. */ 4067 4068 /* Has the user made local mods to the working file? 4069 Note that this compares to the current pristine file, which is 4070 different from fb->old_text_base_path if we have a replaced-with-history 4071 file. However, in the case we had an obstruction, we check against the 4072 new text base. 4073 */ 4074 if (fb->adding_file && !fb->add_existed) 4075 { 4076 is_locally_modified = FALSE; /* There is no file: Don't check */ 4077 } 4078 else 4079 { 4080 /* The working file is not an obstruction. 4081 So: is the file modified, relative to its ORIGINAL pristine? 4082 4083 This function sets is_locally_modified to FALSE for 4084 files that do not exist and for directories. */ 4085 4086 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified, 4087 eb->db, fb->local_abspath, 4088 FALSE /* exact_comparison */, 4089 scratch_pool)); 4090 } 4091 4092 /* For 'textual' merging, we use the following system: 4093 4094 When a file is modified and we have a new BASE: 4095 - For text files 4096 * svn_wc_merge uses diff3 4097 * possibly makes backups and marks files as conflicted. 4098 4099 - For binary files 4100 * svn_wc_merge makes backups and marks files as conflicted. 4101 4102 If a file is not modified and we have a new BASE: 4103 * Install from pristine. 4104 4105 If we have property changes related to magic properties or if the 4106 svn:keywords property is set: 4107 * Retranslate from the working file. 4108 */ 4109 if (! is_locally_modified 4110 && fb->new_text_base_sha1_checksum) 4111 { 4112 /* If there are no local mods, who cares whether it's a text 4113 or binary file! Just write a log command to overwrite 4114 any working file with the new text-base. If newline 4115 conversion or keyword substitution is activated, this 4116 will happen as well during the copy. 4117 For replaced files, though, we want to merge in the changes 4118 even if the file is not modified compared to the (non-revert) 4119 text-base. */ 4120 4121 *install_pristine = TRUE; 4122 } 4123 else if (fb->new_text_base_sha1_checksum) 4124 { 4125 /* Actual file exists and has local mods: 4126 Now we need to let loose svn_wc__merge_internal() to merge 4127 the textual changes into the working file. */ 4128 SVN_ERR(svn_wc__perform_file_merge(work_items, 4129 conflict_skel, 4130 &found_text_conflict, 4131 eb->db, 4132 fb->local_abspath, 4133 pb->local_abspath, 4134 fb->new_text_base_sha1_checksum, 4135 fb->add_existed 4136 ? NULL 4137 : fb->original_checksum, 4138 actual_props, 4139 eb->ext_patterns, 4140 fb->old_revision, 4141 *eb->target_revision, 4142 fb->propchanges, 4143 eb->diff3_cmd, 4144 eb->cancel_func, eb->cancel_baton, 4145 result_pool, scratch_pool)); 4146 } /* end: working file exists and has mods */ 4147 else 4148 { 4149 /* There is no new text base, but let's see if the working file needs 4150 to be updated for any other reason. */ 4151 4152 apr_hash_t *keywords; 4153 4154 /* Determine if any of the propchanges are the "magic" ones that 4155 might require changing the working file. */ 4156 svn_boolean_t magic_props_changed; 4157 4158 magic_props_changed = svn_wc__has_magic_property(fb->propchanges); 4159 4160 SVN_ERR(svn_wc__get_translate_info(NULL, NULL, 4161 &keywords, 4162 NULL, 4163 eb->db, fb->local_abspath, 4164 actual_props, TRUE, 4165 scratch_pool, scratch_pool)); 4166 if (magic_props_changed || keywords) 4167 { 4168 /* Special edge-case: it's possible that this file installation 4169 only involves propchanges, but that some of those props still 4170 require a retranslation of the working file. 4171 4172 OR that the file doesn't involve propchanges which by themselves 4173 require retranslation, but receiving a change bumps the revision 4174 number which requires re-expansion of keywords... */ 4175 4176 if (is_locally_modified) 4177 { 4178 const char *tmptext; 4179 4180 /* Copy and DEtranslate the working file to a temp text-base. 4181 Note that detranslation is done according to the old props. */ 4182 SVN_ERR(svn_wc__internal_translated_file( 4183 &tmptext, fb->local_abspath, eb->db, fb->local_abspath, 4184 SVN_WC_TRANSLATE_TO_NF 4185 | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP, 4186 eb->cancel_func, eb->cancel_baton, 4187 result_pool, scratch_pool)); 4188 4189 /* We always want to reinstall the working file if the magic 4190 properties have changed, or there are any keywords present. 4191 Note that TMPTEXT might actually refer to the working file 4192 itself (the above function skips a detranslate when not 4193 required). This is acceptable, as we will (re)translate 4194 according to the new properties into a temporary file (from 4195 the working file), and then rename the temp into place. Magic! 4196 */ 4197 *install_pristine = TRUE; 4198 *install_from = tmptext; 4199 } 4200 else 4201 { 4202 /* Use our existing 'copy' from the pristine store instead 4203 of making a new copy. This way we can use the standard code 4204 to update the recorded size and modification time. 4205 (Issue #3842) */ 4206 *install_pristine = TRUE; 4207 } 4208 } 4209 } 4210 4211 /* Set the returned content state. */ 4212 4213 if (found_text_conflict) 4214 *content_state = svn_wc_notify_state_conflicted; 4215 else if (fb->new_text_base_sha1_checksum) 4216 { 4217 if (is_locally_modified) 4218 *content_state = svn_wc_notify_state_merged; 4219 else 4220 *content_state = svn_wc_notify_state_changed; 4221 } 4222 else 4223 *content_state = svn_wc_notify_state_unchanged; 4224 4225 return SVN_NO_ERROR; 4226} 4227 4228 4229/* An svn_delta_editor_t function. */ 4230/* Mostly a wrapper around merge_file. */ 4231static svn_error_t * 4232close_file(void *file_baton, 4233 const char *expected_md5_digest, 4234 apr_pool_t *pool) 4235{ 4236 struct file_baton *fb = file_baton; 4237 struct dir_baton *pdb = fb->dir_baton; 4238 struct edit_baton *eb = fb->edit_baton; 4239 svn_wc_notify_state_t content_state, prop_state; 4240 svn_wc_notify_lock_state_t lock_state; 4241 svn_checksum_t *expected_md5_checksum = NULL; 4242 apr_hash_t *new_base_props = NULL; 4243 apr_hash_t *new_actual_props = NULL; 4244 apr_array_header_t *entry_prop_changes; 4245 apr_array_header_t *dav_prop_changes; 4246 apr_array_header_t *regular_prop_changes; 4247 apr_hash_t *current_base_props = NULL; 4248 apr_hash_t *current_actual_props = NULL; 4249 apr_hash_t *local_actual_props = NULL; 4250 svn_skel_t *all_work_items = NULL; 4251 svn_skel_t *conflict_skel = NULL; 4252 svn_skel_t *work_item; 4253 apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */ 4254 svn_boolean_t keep_recorded_info = FALSE; 4255 const svn_checksum_t *new_checksum; 4256 apr_array_header_t *iprops = NULL; 4257 4258 if (fb->skip_this) 4259 { 4260 svn_pool_destroy(fb->pool); 4261 SVN_ERR(maybe_release_dir_info(pdb)); 4262 return SVN_NO_ERROR; 4263 } 4264 4265 if (fb->edited) 4266 conflict_skel = fb->edit_conflict; 4267 4268 if (expected_md5_digest) 4269 SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5, 4270 expected_md5_digest, scratch_pool)); 4271 4272 if (fb->new_text_base_md5_checksum && expected_md5_checksum 4273 && !svn_checksum_match(expected_md5_checksum, 4274 fb->new_text_base_md5_checksum)) 4275 return svn_error_trace( 4276 svn_checksum_mismatch_err(expected_md5_checksum, 4277 fb->new_text_base_md5_checksum, 4278 scratch_pool, 4279 _("Checksum mismatch for '%s'"), 4280 svn_dirent_local_style( 4281 fb->local_abspath, pool))); 4282 4283 /* Gather the changes for each kind of property. */ 4284 SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes, 4285 &dav_prop_changes, ®ular_prop_changes, 4286 scratch_pool)); 4287 4288 /* Extract the changed_* and lock state information. */ 4289 { 4290 svn_revnum_t new_changed_rev; 4291 apr_time_t new_changed_date; 4292 const char *new_changed_author; 4293 4294 SVN_ERR(accumulate_last_change(&new_changed_rev, 4295 &new_changed_date, 4296 &new_changed_author, 4297 entry_prop_changes, 4298 scratch_pool, scratch_pool)); 4299 4300 if (SVN_IS_VALID_REVNUM(new_changed_rev)) 4301 fb->changed_rev = new_changed_rev; 4302 if (new_changed_date != 0) 4303 fb->changed_date = new_changed_date; 4304 if (new_changed_author != NULL) 4305 fb->changed_author = new_changed_author; 4306 } 4307 4308 /* Determine whether the file has become unlocked. */ 4309 { 4310 int i; 4311 4312 lock_state = svn_wc_notify_lock_state_unchanged; 4313 4314 for (i = 0; i < entry_prop_changes->nelts; ++i) 4315 { 4316 const svn_prop_t *prop 4317 = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t); 4318 4319 /* If we see a change to the LOCK_TOKEN entry prop, then the only 4320 possible change is its REMOVAL. Thus, the lock has been removed, 4321 and we should likewise remove our cached copy of it. */ 4322 if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN)) 4323 { 4324 /* If we lose the lock, but not because we are switching to 4325 another url, remove the state lock from the wc */ 4326 if (! eb->switch_relpath 4327 || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0) 4328 { 4329 SVN_ERR_ASSERT(prop->value == NULL); 4330 SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, 4331 scratch_pool)); 4332 4333 lock_state = svn_wc_notify_lock_state_unlocked; 4334 } 4335 break; 4336 } 4337 } 4338 } 4339 4340 /* Install all kinds of properties. It is important to do this before 4341 any file content merging, since that process might expand keywords, in 4342 which case we want the new entryprops to be in place. */ 4343 4344 /* Write log commands to merge REGULAR_PROPS into the existing 4345 properties of FB->LOCAL_ABSPATH. Update *PROP_STATE to reflect 4346 the result of the regular prop merge. 4347 4348 BASE_PROPS and WORKING_PROPS are hashes of the base and 4349 working props of the file; if NULL they are read from the wc. */ 4350 4351 /* ### some of this feels like voodoo... */ 4352 4353 if ((!fb->adding_file || fb->add_existed) 4354 && !fb->shadowed) 4355 SVN_ERR(svn_wc__get_actual_props(&local_actual_props, 4356 eb->db, fb->local_abspath, 4357 scratch_pool, scratch_pool)); 4358 if (local_actual_props == NULL) 4359 local_actual_props = apr_hash_make(scratch_pool); 4360 4361 if (fb->add_existed) 4362 { 4363 /* This node already exists. Grab the current pristine properties. */ 4364 SVN_ERR(svn_wc__db_read_pristine_props(¤t_base_props, 4365 eb->db, fb->local_abspath, 4366 scratch_pool, scratch_pool)); 4367 current_actual_props = local_actual_props; 4368 } 4369 else if (!fb->adding_file) 4370 { 4371 /* Get the BASE properties for proper merging. */ 4372 SVN_ERR(svn_wc__db_base_get_props(¤t_base_props, 4373 eb->db, fb->local_abspath, 4374 scratch_pool, scratch_pool)); 4375 current_actual_props = local_actual_props; 4376 } 4377 4378 /* Note: even if the node existed before, it may not have 4379 pristine props (e.g a local-add) */ 4380 if (current_base_props == NULL) 4381 current_base_props = apr_hash_make(scratch_pool); 4382 4383 /* And new nodes need an empty set of ACTUAL props. */ 4384 if (current_actual_props == NULL) 4385 current_actual_props = apr_hash_make(scratch_pool); 4386 4387 prop_state = svn_wc_notify_state_unknown; 4388 4389 if (! fb->shadowed) 4390 { 4391 svn_boolean_t install_pristine; 4392 const char *install_from = NULL; 4393 4394 /* Merge the 'regular' props into the existing working proplist. */ 4395 /* This will merge the old and new props into a new prop db, and 4396 write <cp> commands to the logfile to install the merged 4397 props. */ 4398 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes, 4399 scratch_pool); 4400 SVN_ERR(svn_wc__merge_props(&conflict_skel, 4401 &prop_state, 4402 &new_actual_props, 4403 eb->db, 4404 fb->local_abspath, 4405 NULL /* server_baseprops (update, not merge) */, 4406 current_base_props, 4407 current_actual_props, 4408 regular_prop_changes, /* propchanges */ 4409 scratch_pool, 4410 scratch_pool)); 4411 /* We will ALWAYS have properties to save (after a not-dry-run merge). */ 4412 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL); 4413 4414 /* Merge the text. This will queue some additional work. */ 4415 if (!fb->obstruction_found && !fb->edit_obstructed) 4416 { 4417 svn_error_t *err; 4418 err = merge_file(&work_item, &conflict_skel, 4419 &install_pristine, &install_from, 4420 &content_state, fb, current_actual_props, 4421 fb->changed_date, scratch_pool, scratch_pool); 4422 4423 if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED) 4424 { 4425 if (eb->notify_func) 4426 { 4427 svn_wc_notify_t *notify =svn_wc_create_notify( 4428 fb->local_abspath, 4429 svn_wc_notify_update_skip_access_denied, 4430 scratch_pool); 4431 4432 notify->kind = svn_node_file; 4433 notify->err = err; 4434 4435 eb->notify_func(eb->notify_baton, notify, scratch_pool); 4436 } 4437 svn_error_clear(err); 4438 4439 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, 4440 scratch_pool)); 4441 fb->skip_this = TRUE; 4442 4443 svn_pool_destroy(fb->pool); 4444 SVN_ERR(maybe_release_dir_info(pdb)); 4445 return SVN_NO_ERROR; 4446 } 4447 else 4448 SVN_ERR(err); 4449 4450 all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4451 scratch_pool); 4452 } 4453 else 4454 { 4455 install_pristine = FALSE; 4456 if (fb->new_text_base_sha1_checksum) 4457 content_state = svn_wc_notify_state_changed; 4458 else 4459 content_state = svn_wc_notify_state_unchanged; 4460 } 4461 4462 if (install_pristine) 4463 { 4464 svn_boolean_t record_fileinfo; 4465 4466 /* If we are installing from the pristine contents, then go ahead and 4467 record the fileinfo. That will be the "proper" values. Installing 4468 from some random file means the fileinfo does NOT correspond to 4469 the pristine (in which case, the fileinfo will be cleared for 4470 safety's sake). */ 4471 record_fileinfo = (install_from == NULL); 4472 4473 SVN_ERR(svn_wc__wq_build_file_install(&work_item, 4474 eb->db, 4475 fb->local_abspath, 4476 install_from, 4477 eb->use_commit_times, 4478 record_fileinfo, 4479 scratch_pool, scratch_pool)); 4480 all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4481 scratch_pool); 4482 } 4483 else if (lock_state == svn_wc_notify_lock_state_unlocked 4484 && !fb->obstruction_found) 4485 { 4486 /* If a lock was removed and we didn't update the text contents, we 4487 might need to set the file read-only. 4488 4489 Note: this will also update the executable flag, but ... meh. */ 4490 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db, 4491 fb->local_abspath, 4492 scratch_pool, scratch_pool)); 4493 all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4494 scratch_pool); 4495 } 4496 4497 if (! install_pristine 4498 && (content_state == svn_wc_notify_state_unchanged)) 4499 { 4500 /* It is safe to keep the current recorded timestamp and size */ 4501 keep_recorded_info = TRUE; 4502 } 4503 4504 /* Clean up any temporary files. */ 4505 4506 /* Remove the INSTALL_FROM file, as long as it doesn't refer to the 4507 working file. */ 4508 if (install_from != NULL 4509 && strcmp(install_from, fb->local_abspath) != 0) 4510 { 4511 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db, 4512 fb->local_abspath, install_from, 4513 scratch_pool, scratch_pool)); 4514 all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4515 scratch_pool); 4516 } 4517 } 4518 else 4519 { 4520 /* Adding or updating a BASE node under a locally added node. */ 4521 apr_hash_t *fake_actual_props; 4522 4523 if (fb->adding_file) 4524 fake_actual_props = apr_hash_make(scratch_pool); 4525 else 4526 fake_actual_props = current_base_props; 4527 4528 /* Store the incoming props (sent as propchanges) in new_base_props 4529 and create a set of new actual props to use for notifications */ 4530 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes, 4531 scratch_pool); 4532 SVN_ERR(svn_wc__merge_props(&conflict_skel, 4533 &prop_state, 4534 &new_actual_props, 4535 eb->db, 4536 fb->local_abspath, 4537 NULL /* server_baseprops (not merging) */, 4538 current_base_props /* pristine_props */, 4539 fake_actual_props /* actual_props */, 4540 regular_prop_changes, /* propchanges */ 4541 scratch_pool, 4542 scratch_pool)); 4543 4544 if (fb->new_text_base_sha1_checksum) 4545 content_state = svn_wc_notify_state_changed; 4546 else 4547 content_state = svn_wc_notify_state_unchanged; 4548 } 4549 4550 /* Insert/replace the BASE node with all of the new metadata. */ 4551 4552 /* Set the 'checksum' column of the file's BASE_NODE row to 4553 * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that 4554 * checksum is already in the pristine store. */ 4555 new_checksum = fb->new_text_base_sha1_checksum; 4556 4557 /* If we don't have a NEW checksum, then the base must not have changed. 4558 Just carry over the old checksum. */ 4559 if (new_checksum == NULL) 4560 new_checksum = fb->original_checksum; 4561 4562 if (conflict_skel) 4563 { 4564 SVN_ERR(complete_conflict(conflict_skel, 4565 fb->edit_baton, 4566 fb->local_abspath, 4567 fb->old_repos_relpath, 4568 fb->old_revision, 4569 fb->new_relpath, 4570 svn_node_file, svn_node_file, 4571 fb->pool, scratch_pool)); 4572 4573 SVN_ERR(svn_wc__conflict_create_markers(&work_item, 4574 eb->db, fb->local_abspath, 4575 conflict_skel, 4576 scratch_pool, scratch_pool)); 4577 4578 all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4579 scratch_pool); 4580 } 4581 4582 /* Any inherited props to be set set for this base node? */ 4583 if (eb->wcroot_iprops) 4584 { 4585 iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath); 4586 4587 /* close_edit may also update iprops for switched nodes, catching 4588 those for which close_directory is never called (e.g. a switch 4589 with no changes). So as a minor optimization we remove any 4590 iprops from the hash so as not to set them again in 4591 close_edit. */ 4592 if (iprops) 4593 svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL); 4594 } 4595 4596 SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath, 4597 eb->wcroot_abspath, 4598 fb->new_relpath, 4599 eb->repos_root, eb->repos_uuid, 4600 *eb->target_revision, 4601 new_base_props, 4602 fb->changed_rev, 4603 fb->changed_date, 4604 fb->changed_author, 4605 new_checksum, 4606 (dav_prop_changes->nelts > 0) 4607 ? svn_prop_array_to_hash( 4608 dav_prop_changes, 4609 scratch_pool) 4610 : NULL, 4611 (fb->add_existed && fb->adding_file), 4612 (! fb->shadowed) && new_base_props, 4613 new_actual_props, 4614 iprops, 4615 keep_recorded_info, 4616 (fb->shadowed && fb->obstruction_found), 4617 conflict_skel, 4618 all_work_items, 4619 scratch_pool)); 4620 4621 if (conflict_skel && eb->conflict_func) 4622 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, 4623 conflict_skel, 4624 NULL /* merge_options */, 4625 eb->conflict_func, 4626 eb->conflict_baton, 4627 eb->cancel_func, 4628 eb->cancel_baton, 4629 scratch_pool)); 4630 4631 /* Deal with the WORKING tree, based on updates to the BASE tree. */ 4632 4633 svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL); 4634 4635 /* Send a notification to the callback function. (Skip notifications 4636 about files which were already notified for another reason.) */ 4637 if (eb->notify_func && !fb->already_notified 4638 && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked)) 4639 { 4640 svn_wc_notify_t *notify; 4641 svn_wc_notify_action_t action = svn_wc_notify_update_update; 4642 4643 if (fb->edited) 4644 { 4645 if (fb->shadowed || fb->edit_obstructed) 4646 action = fb->adding_file 4647 ? svn_wc_notify_update_shadowed_add 4648 : svn_wc_notify_update_shadowed_update; 4649 else if (fb->obstruction_found || fb->add_existed) 4650 { 4651 if (content_state != svn_wc_notify_state_conflicted) 4652 action = svn_wc_notify_exists; 4653 } 4654 else if (fb->adding_file) 4655 { 4656 action = svn_wc_notify_update_add; 4657 } 4658 } 4659 else 4660 { 4661 SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked); 4662 action = svn_wc_notify_update_broken_lock; 4663 } 4664 4665 /* If the file was moved-away, notify for the moved-away node. 4666 * The original location only had its BASE info changed and 4667 * we don't usually notify about such changes. */ 4668 notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool); 4669 notify->kind = svn_node_file; 4670 notify->content_state = content_state; 4671 notify->prop_state = prop_state; 4672 notify->lock_state = lock_state; 4673 notify->revision = *eb->target_revision; 4674 notify->old_revision = fb->old_revision; 4675 4676 /* Fetch the mimetype from the actual properties */ 4677 notify->mime_type = svn_prop_get_value(new_actual_props, 4678 SVN_PROP_MIME_TYPE); 4679 4680 eb->notify_func(eb->notify_baton, notify, scratch_pool); 4681 } 4682 4683 svn_pool_destroy(fb->pool); /* Destroy scratch_pool */ 4684 4685 /* We have one less referrer to the directory */ 4686 SVN_ERR(maybe_release_dir_info(pdb)); 4687 4688 return SVN_NO_ERROR; 4689} 4690 4691 4692/* An svn_delta_editor_t function. */ 4693static svn_error_t * 4694close_edit(void *edit_baton, 4695 apr_pool_t *pool) 4696{ 4697 struct edit_baton *eb = edit_baton; 4698 apr_pool_t *scratch_pool = eb->pool; 4699 4700 /* The editor didn't even open the root; we have to take care of 4701 some cleanup stuffs. */ 4702 if (! eb->root_opened 4703 && *eb->target_basename == '\0') 4704 { 4705 /* We need to "un-incomplete" the root directory. */ 4706 SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db, 4707 eb->anchor_abspath, 4708 scratch_pool)); 4709 } 4710 4711 /* By definition, anybody "driving" this editor for update or switch 4712 purposes at a *minimum* must have called set_target_revision() at 4713 the outset, and close_edit() at the end -- even if it turned out 4714 that no changes ever had to be made, and open_root() was never 4715 called. That's fine. But regardless, when the edit is over, 4716 this editor needs to make sure that *all* paths have had their 4717 revisions bumped to the new target revision. */ 4718 4719 /* Make sure our update target now has the new working revision. 4720 Also, if this was an 'svn switch', then rewrite the target's 4721 url. All of this tweaking might happen recursively! Note 4722 that if eb->target is NULL, that's okay (albeit "sneaky", 4723 some might say). */ 4724 4725 /* Extra check: if the update did nothing but make its target 4726 'deleted', then do *not* run cleanup on the target, as it 4727 will only remove the deleted entry! */ 4728 if (! eb->target_deleted) 4729 { 4730 SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db, 4731 eb->target_abspath, 4732 eb->requested_depth, 4733 eb->switch_relpath, 4734 eb->repos_root, 4735 eb->repos_uuid, 4736 *(eb->target_revision), 4737 eb->skipped_trees, 4738 eb->wcroot_iprops, 4739 eb->notify_func, 4740 eb->notify_baton, 4741 eb->pool)); 4742 4743 if (*eb->target_basename != '\0') 4744 { 4745 svn_wc__db_status_t status; 4746 svn_error_t *err; 4747 4748 /* Note: we are fetching information about the *target*, not anchor. 4749 There is no guarantee that the target has a BASE node. 4750 For example: 4751 4752 The node was not present in BASE, but locally-added, and the 4753 update did not create a new BASE node "under" the local-add. 4754 4755 If there is no BASE node for the target, then we certainly don't 4756 have to worry about removing it. */ 4757 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL, 4758 NULL, NULL, NULL, NULL, NULL, NULL, 4759 NULL, NULL, NULL, NULL, 4760 eb->db, eb->target_abspath, 4761 scratch_pool, scratch_pool); 4762 if (err) 4763 { 4764 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 4765 return svn_error_trace(err); 4766 4767 svn_error_clear(err); 4768 } 4769 else if (status == svn_wc__db_status_excluded) 4770 { 4771 /* There is a small chance that the explicit target of an update/ 4772 switch is gone in the repository, in that specific case the 4773 node hasn't been re-added to the BASE tree by this update. 4774 4775 If so, we should get rid of this excluded node now. */ 4776 4777 SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath, 4778 FALSE /* keep_as_working */, 4779 FALSE /* queue_deletes */, 4780 FALSE /* remove_locks */, 4781 SVN_INVALID_REVNUM, 4782 NULL, NULL, scratch_pool)); 4783 } 4784 } 4785 } 4786 4787 /* The edit is over: run the wq with proper cancel support, 4788 but first kill the handler that would run it on the pool 4789 cleanup at the end of this function. */ 4790 apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton); 4791 4792 SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath, 4793 eb->cancel_func, eb->cancel_baton, 4794 eb->pool)); 4795 4796 /* The edit is over, free its pool. 4797 ### No, this is wrong. Who says this editor/baton won't be used 4798 again? But the change is not merely to remove this call. We 4799 should also make eb->pool not be a subpool (see make_editor), 4800 and change callers of svn_client_{checkout,update,switch} to do 4801 better pool management. ### */ 4802 4803 svn_pool_destroy(eb->pool); 4804 4805 return SVN_NO_ERROR; 4806} 4807 4808 4809/*** Returning editors. ***/ 4810 4811/* Helper for the three public editor-supplying functions. */ 4812static svn_error_t * 4813make_editor(svn_revnum_t *target_revision, 4814 svn_wc__db_t *db, 4815 const char *anchor_abspath, 4816 const char *target_basename, 4817 apr_hash_t *wcroot_iprops, 4818 svn_boolean_t use_commit_times, 4819 const char *switch_url, 4820 svn_depth_t depth, 4821 svn_boolean_t depth_is_sticky, 4822 svn_boolean_t allow_unver_obstructions, 4823 svn_boolean_t adds_as_modification, 4824 svn_boolean_t server_performs_filtering, 4825 svn_boolean_t clean_checkout, 4826 svn_wc_notify_func2_t notify_func, 4827 void *notify_baton, 4828 svn_cancel_func_t cancel_func, 4829 void *cancel_baton, 4830 svn_wc_dirents_func_t fetch_dirents_func, 4831 void *fetch_dirents_baton, 4832 svn_wc_conflict_resolver_func2_t conflict_func, 4833 void *conflict_baton, 4834 svn_wc_external_update_t external_func, 4835 void *external_baton, 4836 const char *diff3_cmd, 4837 const apr_array_header_t *preserved_exts, 4838 const svn_delta_editor_t **editor, 4839 void **edit_baton, 4840 apr_pool_t *result_pool, 4841 apr_pool_t *scratch_pool) 4842{ 4843 struct edit_baton *eb; 4844 void *inner_baton; 4845 apr_pool_t *edit_pool = svn_pool_create(result_pool); 4846 svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool); 4847 const svn_delta_editor_t *inner_editor; 4848 const char *repos_root, *repos_uuid; 4849 struct svn_wc__shim_fetch_baton_t *sfb; 4850 svn_delta_shim_callbacks_t *shim_callbacks = 4851 svn_delta_shim_callbacks_default(edit_pool); 4852 4853 /* An unknown depth can't be sticky. */ 4854 if (depth == svn_depth_unknown) 4855 depth_is_sticky = FALSE; 4856 4857 /* Get the anchor's repository root and uuid. The anchor must already exist 4858 in BASE. */ 4859 SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid, 4860 db, anchor_abspath, 4861 result_pool, scratch_pool)); 4862 4863 /* With WC-NG we need a valid repository root */ 4864 SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL); 4865 4866 /* Disallow a switch operation to change the repository root of the target, 4867 if that is known. */ 4868 if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url)) 4869 return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL, 4870 _("'%s'\nis not the same repository as\n'%s'"), 4871 switch_url, repos_root); 4872 4873 /* Construct an edit baton. */ 4874 eb = apr_pcalloc(edit_pool, sizeof(*eb)); 4875 eb->pool = edit_pool; 4876 eb->use_commit_times = use_commit_times; 4877 eb->target_revision = target_revision; 4878 eb->repos_root = repos_root; 4879 eb->repos_uuid = repos_uuid; 4880 eb->db = db; 4881 eb->target_basename = target_basename; 4882 eb->anchor_abspath = anchor_abspath; 4883 eb->wcroot_iprops = wcroot_iprops; 4884 4885 SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath, 4886 edit_pool, scratch_pool)); 4887 4888 if (switch_url) 4889 eb->switch_relpath = 4890 svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool); 4891 else 4892 eb->switch_relpath = NULL; 4893 4894 if (svn_path_is_empty(target_basename)) 4895 eb->target_abspath = eb->anchor_abspath; 4896 else 4897 eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename, 4898 edit_pool); 4899 4900 eb->requested_depth = depth; 4901 eb->depth_is_sticky = depth_is_sticky; 4902 eb->notify_func = notify_func; 4903 eb->notify_baton = notify_baton; 4904 eb->external_func = external_func; 4905 eb->external_baton = external_baton; 4906 eb->diff3_cmd = diff3_cmd; 4907 eb->cancel_func = cancel_func; 4908 eb->cancel_baton = cancel_baton; 4909 eb->conflict_func = conflict_func; 4910 eb->conflict_baton = conflict_baton; 4911 eb->allow_unver_obstructions = allow_unver_obstructions; 4912 eb->adds_as_modification = adds_as_modification; 4913 eb->clean_checkout = clean_checkout; 4914 eb->skipped_trees = apr_hash_make(edit_pool); 4915 eb->dir_dirents = apr_hash_make(edit_pool); 4916 eb->ext_patterns = preserved_exts; 4917 4918 apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton, 4919 apr_pool_cleanup_null); 4920 4921 /* Construct an editor. */ 4922 tree_editor->set_target_revision = set_target_revision; 4923 tree_editor->open_root = open_root; 4924 tree_editor->delete_entry = delete_entry; 4925 tree_editor->add_directory = add_directory; 4926 tree_editor->open_directory = open_directory; 4927 tree_editor->change_dir_prop = change_dir_prop; 4928 tree_editor->close_directory = close_directory; 4929 tree_editor->absent_directory = absent_directory; 4930 tree_editor->add_file = add_file; 4931 tree_editor->open_file = open_file; 4932 tree_editor->apply_textdelta = apply_textdelta; 4933 tree_editor->change_file_prop = change_file_prop; 4934 tree_editor->close_file = close_file; 4935 tree_editor->absent_file = absent_file; 4936 tree_editor->close_edit = close_edit; 4937 4938 /* Fiddle with the type system. */ 4939 inner_editor = tree_editor; 4940 inner_baton = eb; 4941 4942 if (!depth_is_sticky 4943 && depth != svn_depth_unknown 4944 && svn_depth_empty <= depth && depth < svn_depth_infinity 4945 && fetch_dirents_func) 4946 { 4947 /* We are asked to perform an update at a depth less than the ambient 4948 depth. In this case the update won't describe additions that would 4949 have been reported if we updated at the ambient depth. */ 4950 svn_error_t *err; 4951 svn_node_kind_t dir_kind; 4952 svn_wc__db_status_t dir_status; 4953 const char *dir_repos_relpath; 4954 svn_depth_t dir_depth; 4955 4956 /* we have to do this on the target of the update, not the anchor */ 4957 err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL, 4958 &dir_repos_relpath, NULL, NULL, NULL, 4959 NULL, NULL, &dir_depth, NULL, NULL, NULL, 4960 NULL, NULL, NULL, 4961 db, eb->target_abspath, 4962 scratch_pool, scratch_pool); 4963 4964 if (!err 4965 && dir_kind == svn_node_dir 4966 && dir_status == svn_wc__db_status_normal) 4967 { 4968 if (dir_depth > depth) 4969 { 4970 apr_hash_t *dirents; 4971 4972 /* If we switch, we should look at the new relpath */ 4973 if (eb->switch_relpath) 4974 dir_repos_relpath = eb->switch_relpath; 4975 4976 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, 4977 repos_root, dir_repos_relpath, 4978 edit_pool, scratch_pool)); 4979 4980 if (dirents != NULL && apr_hash_count(dirents)) 4981 svn_hash_sets(eb->dir_dirents, 4982 apr_pstrdup(edit_pool, dir_repos_relpath), 4983 dirents); 4984 } 4985 4986 if (depth == svn_depth_immediates) 4987 { 4988 /* Worst case scenario of issue #3569 fix: We have to do the 4989 same for all existing subdirs, but then we check for 4990 svn_depth_empty. */ 4991 const apr_array_header_t *children; 4992 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 4993 int i; 4994 SVN_ERR(svn_wc__db_base_get_children(&children, db, 4995 eb->target_abspath, 4996 scratch_pool, 4997 iterpool)); 4998 4999 for (i = 0; i < children->nelts; i++) 5000 { 5001 const char *child_abspath; 5002 const char *child_name; 5003 5004 svn_pool_clear(iterpool); 5005 5006 child_name = APR_ARRAY_IDX(children, i, const char *); 5007 5008 child_abspath = svn_dirent_join(eb->target_abspath, 5009 child_name, iterpool); 5010 5011 SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind, 5012 NULL, &dir_repos_relpath, 5013 NULL, NULL, NULL, NULL, 5014 NULL, &dir_depth, NULL, 5015 NULL, NULL, NULL, NULL, 5016 NULL, 5017 db, child_abspath, 5018 iterpool, iterpool)); 5019 5020 if (dir_kind == svn_node_dir 5021 && dir_status == svn_wc__db_status_normal 5022 && dir_depth > svn_depth_empty) 5023 { 5024 apr_hash_t *dirents; 5025 5026 /* If we switch, we should look at the new relpath */ 5027 if (eb->switch_relpath) 5028 dir_repos_relpath = svn_relpath_join( 5029 eb->switch_relpath, 5030 child_name, iterpool); 5031 5032 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, 5033 repos_root, dir_repos_relpath, 5034 edit_pool, iterpool)); 5035 5036 if (dirents != NULL && apr_hash_count(dirents)) 5037 svn_hash_sets(eb->dir_dirents, 5038 apr_pstrdup(edit_pool, 5039 dir_repos_relpath), 5040 dirents); 5041 } 5042 } 5043 } 5044 } 5045 else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 5046 svn_error_clear(err); 5047 else 5048 SVN_ERR(err); 5049 } 5050 5051 /* We need to limit the scope of our operation to the ambient depths 5052 present in the working copy already, but only if the requested 5053 depth is not sticky. If a depth was explicitly requested, 5054 libsvn_delta/depth_filter_editor.c will ensure that we never see 5055 editor calls that extend beyond the scope of the requested depth. 5056 But even what we do so might extend beyond the scope of our 5057 ambient depth. So we use another filtering editor to avoid 5058 modifying the ambient working copy depth when not asked to do so. 5059 (This can also be skipped if the server understands depth.) */ 5060 if (!server_performs_filtering 5061 && !depth_is_sticky) 5062 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor, 5063 &inner_baton, 5064 db, 5065 anchor_abspath, 5066 target_basename, 5067 inner_editor, 5068 inner_baton, 5069 result_pool)); 5070 5071 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, 5072 cancel_baton, 5073 inner_editor, 5074 inner_baton, 5075 editor, 5076 edit_baton, 5077 result_pool)); 5078 5079 sfb = apr_palloc(result_pool, sizeof(*sfb)); 5080 sfb->db = db; 5081 sfb->base_abspath = eb->anchor_abspath; 5082 sfb->fetch_base = TRUE; 5083 5084 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func; 5085 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func; 5086 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func; 5087 shim_callbacks->fetch_baton = sfb; 5088 5089 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, 5090 NULL, NULL, shim_callbacks, 5091 result_pool, scratch_pool)); 5092 5093 return SVN_NO_ERROR; 5094} 5095 5096 5097svn_error_t * 5098svn_wc__get_update_editor(const svn_delta_editor_t **editor, 5099 void **edit_baton, 5100 svn_revnum_t *target_revision, 5101 svn_wc_context_t *wc_ctx, 5102 const char *anchor_abspath, 5103 const char *target_basename, 5104 apr_hash_t *wcroot_iprops, 5105 svn_boolean_t use_commit_times, 5106 svn_depth_t depth, 5107 svn_boolean_t depth_is_sticky, 5108 svn_boolean_t allow_unver_obstructions, 5109 svn_boolean_t adds_as_modification, 5110 svn_boolean_t server_performs_filtering, 5111 svn_boolean_t clean_checkout, 5112 const char *diff3_cmd, 5113 const apr_array_header_t *preserved_exts, 5114 svn_wc_dirents_func_t fetch_dirents_func, 5115 void *fetch_dirents_baton, 5116 svn_wc_conflict_resolver_func2_t conflict_func, 5117 void *conflict_baton, 5118 svn_wc_external_update_t external_func, 5119 void *external_baton, 5120 svn_cancel_func_t cancel_func, 5121 void *cancel_baton, 5122 svn_wc_notify_func2_t notify_func, 5123 void *notify_baton, 5124 apr_pool_t *result_pool, 5125 apr_pool_t *scratch_pool) 5126{ 5127 return make_editor(target_revision, wc_ctx->db, anchor_abspath, 5128 target_basename, wcroot_iprops, use_commit_times, 5129 NULL, depth, depth_is_sticky, allow_unver_obstructions, 5130 adds_as_modification, server_performs_filtering, 5131 clean_checkout, 5132 notify_func, notify_baton, 5133 cancel_func, cancel_baton, 5134 fetch_dirents_func, fetch_dirents_baton, 5135 conflict_func, conflict_baton, 5136 external_func, external_baton, 5137 diff3_cmd, preserved_exts, editor, edit_baton, 5138 result_pool, scratch_pool); 5139} 5140 5141svn_error_t * 5142svn_wc__get_switch_editor(const svn_delta_editor_t **editor, 5143 void **edit_baton, 5144 svn_revnum_t *target_revision, 5145 svn_wc_context_t *wc_ctx, 5146 const char *anchor_abspath, 5147 const char *target_basename, 5148 const char *switch_url, 5149 apr_hash_t *wcroot_iprops, 5150 svn_boolean_t use_commit_times, 5151 svn_depth_t depth, 5152 svn_boolean_t depth_is_sticky, 5153 svn_boolean_t allow_unver_obstructions, 5154 svn_boolean_t server_performs_filtering, 5155 const char *diff3_cmd, 5156 const apr_array_header_t *preserved_exts, 5157 svn_wc_dirents_func_t fetch_dirents_func, 5158 void *fetch_dirents_baton, 5159 svn_wc_conflict_resolver_func2_t conflict_func, 5160 void *conflict_baton, 5161 svn_wc_external_update_t external_func, 5162 void *external_baton, 5163 svn_cancel_func_t cancel_func, 5164 void *cancel_baton, 5165 svn_wc_notify_func2_t notify_func, 5166 void *notify_baton, 5167 apr_pool_t *result_pool, 5168 apr_pool_t *scratch_pool) 5169{ 5170 SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool)); 5171 5172 return make_editor(target_revision, wc_ctx->db, anchor_abspath, 5173 target_basename, wcroot_iprops, use_commit_times, 5174 switch_url, 5175 depth, depth_is_sticky, allow_unver_obstructions, 5176 FALSE /* adds_as_modification */, 5177 server_performs_filtering, 5178 FALSE /* clean_checkout */, 5179 notify_func, notify_baton, 5180 cancel_func, cancel_baton, 5181 fetch_dirents_func, fetch_dirents_baton, 5182 conflict_func, conflict_baton, 5183 external_func, external_baton, 5184 diff3_cmd, preserved_exts, 5185 editor, edit_baton, 5186 result_pool, scratch_pool); 5187} 5188 5189 5190 5191/* ### Note that this function is completely different from the rest of the 5192 update editor in what it updates. The update editor changes only BASE 5193 and ACTUAL and this function just changes WORKING and ACTUAL. 5194 5195 In the entries world this function shared a lot of code with the 5196 update editor but in the wonderful new WC-NG world it will probably 5197 do more and more by itself and would be more logically grouped with 5198 the add/copy functionality in adm_ops.c and copy.c. */ 5199svn_error_t * 5200svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, 5201 const char *local_abspath, 5202 svn_stream_t *new_base_contents, 5203 svn_stream_t *new_contents, 5204 apr_hash_t *new_base_props, 5205 apr_hash_t *new_props, 5206 const char *copyfrom_url, 5207 svn_revnum_t copyfrom_rev, 5208 svn_cancel_func_t cancel_func, 5209 void *cancel_baton, 5210 apr_pool_t *scratch_pool) 5211{ 5212 svn_wc__db_t *db = wc_ctx->db; 5213 const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 5214 svn_wc__db_status_t status; 5215 svn_node_kind_t kind; 5216 const char *tmp_text_base_abspath; 5217 svn_checksum_t *new_text_base_md5_checksum; 5218 svn_checksum_t *new_text_base_sha1_checksum; 5219 const char *source_abspath = NULL; 5220 svn_skel_t *all_work_items = NULL; 5221 svn_skel_t *work_item; 5222 const char *repos_root_url; 5223 const char *repos_uuid; 5224 const char *original_repos_relpath; 5225 svn_revnum_t changed_rev; 5226 apr_time_t changed_date; 5227 const char *changed_author; 5228 svn_error_t *err; 5229 apr_pool_t *pool = scratch_pool; 5230 5231 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 5232 SVN_ERR_ASSERT(new_base_contents != NULL); 5233 SVN_ERR_ASSERT(new_base_props != NULL); 5234 5235 /* We should have a write lock on this file's parent directory. */ 5236 SVN_ERR(svn_wc__write_check(db, dir_abspath, pool)); 5237 5238 err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5239 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5240 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5241 NULL, NULL, NULL, 5242 db, local_abspath, scratch_pool, scratch_pool); 5243 5244 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 5245 return svn_error_trace(err); 5246 else if(err) 5247 svn_error_clear(err); 5248 else 5249 switch (status) 5250 { 5251 case svn_wc__db_status_not_present: 5252 case svn_wc__db_status_deleted: 5253 break; 5254 default: 5255 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, 5256 _("Node '%s' exists."), 5257 svn_dirent_local_style(local_abspath, 5258 scratch_pool)); 5259 } 5260 5261 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url, 5262 &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, 5263 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5264 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5265 db, dir_abspath, scratch_pool, scratch_pool)); 5266 5267 switch (status) 5268 { 5269 case svn_wc__db_status_normal: 5270 case svn_wc__db_status_added: 5271 break; 5272 case svn_wc__db_status_deleted: 5273 return 5274 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL, 5275 _("Can't add '%s' to a parent directory" 5276 " scheduled for deletion"), 5277 svn_dirent_local_style(local_abspath, 5278 scratch_pool)); 5279 default: 5280 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err, 5281 _("Can't find parent directory's node while" 5282 " trying to add '%s'"), 5283 svn_dirent_local_style(local_abspath, 5284 scratch_pool)); 5285 } 5286 if (kind != svn_node_dir) 5287 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 5288 _("Can't schedule an addition of '%s'" 5289 " below a not-directory node"), 5290 svn_dirent_local_style(local_abspath, 5291 scratch_pool)); 5292 5293 /* Fabricate the anticipated new URL of the target and check the 5294 copyfrom URL to be in the same repository. */ 5295 if (copyfrom_url != NULL) 5296 { 5297 /* Find the repository_root via the parent directory, which 5298 is always versioned before this function is called */ 5299 5300 if (!repos_root_url) 5301 { 5302 /* The parent is an addition, scan upwards to find the right info */ 5303 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, 5304 &repos_root_url, &repos_uuid, 5305 NULL, NULL, NULL, NULL, 5306 wc_ctx->db, dir_abspath, 5307 scratch_pool, scratch_pool)); 5308 } 5309 SVN_ERR_ASSERT(repos_root_url); 5310 5311 original_repos_relpath = 5312 svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool); 5313 5314 if (!original_repos_relpath) 5315 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 5316 _("Copyfrom-url '%s' has different repository" 5317 " root than '%s'"), 5318 copyfrom_url, repos_root_url); 5319 } 5320 else 5321 { 5322 original_repos_relpath = NULL; 5323 copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */ 5324 } 5325 5326 /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and 5327 filter NEW_BASE_PROPS so it contains only regular props. */ 5328 { 5329 apr_array_header_t *regular_props; 5330 apr_array_header_t *entry_props; 5331 5332 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool), 5333 &entry_props, NULL, ®ular_props, 5334 pool)); 5335 5336 /* Put regular props back into a hash table. */ 5337 new_base_props = svn_prop_array_to_hash(regular_props, pool); 5338 5339 /* Get the change_* info from the entry props. */ 5340 SVN_ERR(accumulate_last_change(&changed_rev, 5341 &changed_date, 5342 &changed_author, 5343 entry_props, pool, pool)); 5344 } 5345 5346 /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to 5347 it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its 5348 NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */ 5349 { 5350 svn_stream_t *tmp_base_contents; 5351 5352 SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents, 5353 &tmp_text_base_abspath, 5354 &new_text_base_md5_checksum, 5355 &new_text_base_sha1_checksum, 5356 wc_ctx->db, local_abspath, 5357 pool, pool)); 5358 SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents, 5359 cancel_func, cancel_baton, pool)); 5360 } 5361 5362 /* If the caller gave us a new working file, copy it to a safe (temporary) 5363 location and set SOURCE_ABSPATH to that path. We'll then translate/copy 5364 that into place after the node's state has been created. */ 5365 if (new_contents) 5366 { 5367 const char *temp_dir_abspath; 5368 svn_stream_t *tmp_contents; 5369 5370 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, 5371 local_abspath, pool, pool)); 5372 SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath, 5373 temp_dir_abspath, svn_io_file_del_none, 5374 pool, pool)); 5375 SVN_ERR(svn_stream_copy3(new_contents, tmp_contents, 5376 cancel_func, cancel_baton, pool)); 5377 } 5378 5379 /* Install new text base for copied files. Added files do NOT have a 5380 text base. */ 5381 if (copyfrom_url != NULL) 5382 { 5383 SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath, 5384 new_text_base_sha1_checksum, 5385 new_text_base_md5_checksum, pool)); 5386 } 5387 else 5388 { 5389 /* ### There's something wrong around here. Sometimes (merge from a 5390 foreign repository, at least) we are called with copyfrom_url = 5391 NULL and an empty new_base_contents (and an empty set of 5392 new_base_props). Why an empty "new base"? 5393 5394 That happens in merge_tests.py 54,87,88,89,143. 5395 5396 In that case, having been given this supposed "new base" file, we 5397 copy it and calculate its checksum but do not install it. Why? 5398 That must be wrong. 5399 5400 To crudely work around one issue with this, that we shouldn't 5401 record a checksum in the database if we haven't installed the 5402 corresponding pristine text, for now we'll just set the checksum 5403 to NULL. 5404 5405 The proper solution is probably more like: the caller should pass 5406 NULL for the missing information, and this function should learn to 5407 handle that. */ 5408 5409 new_text_base_sha1_checksum = NULL; 5410 new_text_base_md5_checksum = NULL; 5411 } 5412 5413 /* For added files without NEW_CONTENTS, then generate the working file 5414 from the provided "pristine" contents. */ 5415 if (new_contents == NULL && copyfrom_url == NULL) 5416 source_abspath = tmp_text_base_abspath; 5417 5418 { 5419 svn_boolean_t record_fileinfo; 5420 5421 /* If new contents were provided, then we do NOT want to record the 5422 file information. We assume the new contents do not match the 5423 "proper" values for RECORDED_SIZE and RECORDED_TIME. */ 5424 record_fileinfo = (new_contents == NULL); 5425 5426 /* Install the working copy file (with appropriate translation) from 5427 the appropriate source. SOURCE_ABSPATH will be NULL, indicating an 5428 installation from the pristine (available for copied/moved files), 5429 or it will specify a temporary file where we placed a "pristine" 5430 (for an added file) or a detranslated local-mods file. */ 5431 SVN_ERR(svn_wc__wq_build_file_install(&work_item, 5432 db, local_abspath, 5433 source_abspath, 5434 FALSE /* use_commit_times */, 5435 record_fileinfo, 5436 pool, pool)); 5437 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); 5438 5439 /* If we installed from somewhere besides the official pristine, then 5440 it is a temporary file, which needs to be removed. */ 5441 if (source_abspath != NULL) 5442 { 5443 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath, 5444 source_abspath, 5445 pool, pool)); 5446 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); 5447 } 5448 } 5449 5450 /* ### ideally, we would have a single DB operation, and queue the work 5451 ### items on that. for now, we'll queue them with the second call. */ 5452 5453 SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath, 5454 new_base_props, 5455 changed_rev, 5456 changed_date, 5457 changed_author, 5458 original_repos_relpath, 5459 original_repos_relpath ? repos_root_url 5460 : NULL, 5461 original_repos_relpath ? repos_uuid : NULL, 5462 copyfrom_rev, 5463 new_text_base_sha1_checksum, 5464 TRUE, 5465 new_props, 5466 FALSE /* is_move */, 5467 NULL /* conflict */, 5468 all_work_items, 5469 pool)); 5470 5471 return svn_error_trace(svn_wc__wq_run(db, dir_abspath, 5472 cancel_func, cancel_baton, 5473 pool)); 5474} 5475 5476svn_error_t * 5477svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx, 5478 const char *local_abspath, 5479 apr_hash_t *new_original_props, 5480 const char *copyfrom_url, 5481 svn_revnum_t copyfrom_rev, 5482 apr_pool_t *scratch_pool) 5483{ 5484 svn_wc__db_status_t status; 5485 svn_node_kind_t kind; 5486 const char *original_repos_relpath; 5487 const char *original_root_url; 5488 const char *original_uuid; 5489 svn_boolean_t had_props; 5490 svn_boolean_t props_mod; 5491 5492 svn_revnum_t original_revision; 5493 svn_revnum_t changed_rev; 5494 apr_time_t changed_date; 5495 const char *changed_author; 5496 5497 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 5498 NULL, NULL, NULL, NULL, NULL, 5499 &original_repos_relpath, &original_root_url, 5500 &original_uuid, &original_revision, NULL, NULL, 5501 NULL, NULL, NULL, NULL, &had_props, &props_mod, 5502 NULL, NULL, NULL, 5503 wc_ctx->db, local_abspath, 5504 scratch_pool, scratch_pool)); 5505 5506 if (status != svn_wc__db_status_added 5507 || kind != svn_node_dir 5508 || had_props 5509 || props_mod 5510 || !original_repos_relpath) 5511 { 5512 return svn_error_createf( 5513 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 5514 _("'%s' is not an unmodified copied directory"), 5515 svn_dirent_local_style(local_abspath, scratch_pool)); 5516 } 5517 if (original_revision != copyfrom_rev 5518 || strcmp(copyfrom_url, 5519 svn_path_url_add_component2(original_root_url, 5520 original_repos_relpath, 5521 scratch_pool))) 5522 { 5523 return svn_error_createf( 5524 SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL, 5525 _("Copyfrom '%s' doesn't match original location of '%s'"), 5526 copyfrom_url, 5527 svn_dirent_local_style(local_abspath, scratch_pool)); 5528 } 5529 5530 { 5531 apr_array_header_t *regular_props; 5532 apr_array_header_t *entry_props; 5533 5534 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props, 5535 scratch_pool), 5536 &entry_props, NULL, ®ular_props, 5537 scratch_pool)); 5538 5539 /* Put regular props back into a hash table. */ 5540 new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool); 5541 5542 /* Get the change_* info from the entry props. */ 5543 SVN_ERR(accumulate_last_change(&changed_rev, 5544 &changed_date, 5545 &changed_author, 5546 entry_props, scratch_pool, scratch_pool)); 5547 } 5548 5549 return svn_error_trace( 5550 svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath, 5551 new_original_props, 5552 changed_rev, changed_date, changed_author, 5553 original_repos_relpath, original_root_url, 5554 original_uuid, original_revision, 5555 NULL /* children */, 5556 FALSE /* is_move */, 5557 svn_depth_infinity, 5558 NULL /* conflict */, 5559 NULL /* work_items */, 5560 scratch_pool)); 5561} 5562