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