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