update_editor.c revision 253734
1251881Speter/* 2251881Speter * update_editor.c : main editor for checkouts and updates 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <stdlib.h> 27251881Speter#include <string.h> 28251881Speter 29251881Speter#include <apr_pools.h> 30251881Speter#include <apr_hash.h> 31251881Speter#include <apr_md5.h> 32251881Speter#include <apr_tables.h> 33251881Speter#include <apr_strings.h> 34251881Speter 35251881Speter#include "svn_types.h" 36251881Speter#include "svn_pools.h" 37251881Speter#include "svn_hash.h" 38251881Speter#include "svn_string.h" 39251881Speter#include "svn_dirent_uri.h" 40251881Speter#include "svn_path.h" 41251881Speter#include "svn_error.h" 42251881Speter#include "svn_io.h" 43251881Speter#include "svn_private_config.h" 44251881Speter#include "svn_time.h" 45251881Speter 46251881Speter#include "wc.h" 47251881Speter#include "adm_files.h" 48251881Speter#include "conflicts.h" 49251881Speter#include "translate.h" 50251881Speter#include "workqueue.h" 51251881Speter 52251881Speter#include "private/svn_subr_private.h" 53251881Speter#include "private/svn_wc_private.h" 54251881Speter#include "private/svn_editor.h" 55251881Speter 56251881Speter/* Checks whether a svn_wc__db_status_t indicates whether a node is 57251881Speter present in a working copy. Used by the editor implementation */ 58251881Speter#define IS_NODE_PRESENT(status) \ 59251881Speter ((status) != svn_wc__db_status_server_excluded &&\ 60251881Speter (status) != svn_wc__db_status_excluded && \ 61251881Speter (status) != svn_wc__db_status_not_present) 62251881Speter 63251881Speterstatic svn_error_t * 64251881Speterpath_join_under_root(const char **result_path, 65251881Speter const char *base_path, 66251881Speter const char *add_path, 67251881Speter apr_pool_t *result_pool); 68251881Speter 69251881Speter 70251881Speter/* 71251881Speter * This code handles "checkout" and "update" and "switch". 72251881Speter * A checkout is similar to an update that is only adding new items. 73251881Speter * 74251881Speter * The intended behaviour of "update" and "switch", focusing on the checks 75251881Speter * to be made before applying a change, is: 76251881Speter * 77251881Speter * For each incoming change: 78251881Speter * if target is already in conflict or obstructed: 79251881Speter * skip this change 80251881Speter * else 81251881Speter * if this action will cause a tree conflict: 82251881Speter * record the tree conflict 83251881Speter * skip this change 84251881Speter * else: 85251881Speter * make this change 86251881Speter * 87251881Speter * In more detail: 88251881Speter * 89251881Speter * For each incoming change: 90251881Speter * 91251881Speter * 1. if # Incoming change is inside an item already in conflict: 92251881Speter * a. tree/text/prop change to node beneath tree-conflicted dir 93251881Speter * then # Skip all changes in this conflicted subtree [*1]: 94251881Speter * do not update the Base nor the Working 95251881Speter * notify "skipped because already in conflict" just once 96251881Speter * for the whole conflicted subtree 97251881Speter * 98251881Speter * if # Incoming change affects an item already in conflict: 99251881Speter * b. tree/text/prop change to tree-conflicted dir/file, or 100251881Speter * c. tree change to a text/prop-conflicted file/dir, or 101251881Speter * d. text/prop change to a text/prop-conflicted file/dir [*2], or 102251881Speter * e. tree change to a dir tree containing any conflicts, 103251881Speter * then # Skip this change [*1]: 104251881Speter * do not update the Base nor the Working 105251881Speter * notify "skipped because already in conflict" 106251881Speter * 107251881Speter * 2. if # Incoming change affects an item that's "obstructed": 108251881Speter * a. on-disk node kind doesn't match recorded Working node kind 109251881Speter * (including an absence/presence mis-match), 110251881Speter * then # Skip this change [*1]: 111251881Speter * do not update the Base nor the Working 112251881Speter * notify "skipped because obstructed" 113251881Speter * 114251881Speter * 3. if # Incoming change raises a tree conflict: 115251881Speter * a. tree/text/prop change to node beneath sched-delete dir, or 116251881Speter * b. tree/text/prop change to sched-delete dir/file, or 117251881Speter * c. text/prop change to tree-scheduled dir/file, 118251881Speter * then # Skip this change: 119251881Speter * do not update the Base nor the Working [*3] 120251881Speter * notify "tree conflict" 121251881Speter * 122251881Speter * 4. Apply the change: 123251881Speter * update the Base 124251881Speter * update the Working, possibly raising text/prop conflicts 125251881Speter * notify 126251881Speter * 127251881Speter * Notes: 128251881Speter * 129251881Speter * "Tree change" here refers to an add or delete of the target node, 130251881Speter * including the add or delete part of a copy or move or rename. 131251881Speter * 132251881Speter * [*1] We should skip changes to an entire node, as the base revision number 133251881Speter * applies to the entire node. Not sure how this affects attempts to 134251881Speter * handle text and prop changes separately. 135251881Speter * 136251881Speter * [*2] Details of which combinations of property and text changes conflict 137251881Speter * are not specified here. 138251881Speter * 139251881Speter * [*3] For now, we skip the update, and require the user to: 140251881Speter * - Modify the WC to be compatible with the incoming change; 141251881Speter * - Mark the conflict as resolved; 142251881Speter * - Repeat the update. 143251881Speter * Ideally, it would be possible to resolve any conflict without 144251881Speter * repeating the update. To achieve this, we would have to store the 145251881Speter * necessary data at conflict detection time, and delay the update of 146251881Speter * the Base until the time of resolving. 147251881Speter */ 148251881Speter 149251881Speter 150251881Speter/*** batons ***/ 151251881Speter 152251881Speterstruct edit_baton 153251881Speter{ 154251881Speter /* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the 155251881Speter directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the 156251881Speter target, the values are identical. 157251881Speter 158251881Speter TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if 159251881Speter ANCHOR_ABSPATH is the target */ 160251881Speter const char *target_basename; 161251881Speter 162251881Speter /* Absolute variants of ANCHOR and TARGET */ 163251881Speter const char *anchor_abspath; 164251881Speter const char *target_abspath; 165251881Speter 166251881Speter /* The DB handle for managing the working copy state. */ 167251881Speter svn_wc__db_t *db; 168251881Speter 169251881Speter /* Array of file extension patterns to preserve as extensions in 170251881Speter generated conflict files. */ 171251881Speter const apr_array_header_t *ext_patterns; 172251881Speter 173251881Speter /* Hash mapping const char * absolute working copy paths to depth-first 174251881Speter ordered arrays of svn_prop_inherited_item_t * structures representing 175251881Speter the properties inherited by the base node at that working copy path. 176251881Speter May be NULL. */ 177251881Speter apr_hash_t *wcroot_iprops; 178251881Speter 179251881Speter /* The revision we're targeting...or something like that. This 180251881Speter starts off as a pointer to the revision to which we are updating, 181251881Speter or SVN_INVALID_REVNUM, but by the end of the edit, should be 182251881Speter pointing to the final revision. */ 183251881Speter svn_revnum_t *target_revision; 184251881Speter 185251881Speter /* The requested depth of this edit. */ 186251881Speter svn_depth_t requested_depth; 187251881Speter 188251881Speter /* Is the requested depth merely an operational limitation, or is 189251881Speter also the new sticky ambient depth of the update target? */ 190251881Speter svn_boolean_t depth_is_sticky; 191251881Speter 192251881Speter /* Need to know if the user wants us to overwrite the 'now' times on 193251881Speter edited/added files with the last-commit-time. */ 194251881Speter svn_boolean_t use_commit_times; 195251881Speter 196251881Speter /* Was the root actually opened (was this a non-empty edit)? */ 197251881Speter svn_boolean_t root_opened; 198251881Speter 199251881Speter /* Was the update-target deleted? This is a special situation. */ 200251881Speter svn_boolean_t target_deleted; 201251881Speter 202251881Speter /* Allow unversioned obstructions when adding a path. */ 203251881Speter svn_boolean_t allow_unver_obstructions; 204251881Speter 205251881Speter /* Handle local additions as modifications of new nodes */ 206251881Speter svn_boolean_t adds_as_modification; 207251881Speter 208251881Speter /* If set, we check out into an empty directory. This allows for a number 209251881Speter of conflict checks to be omitted. */ 210251881Speter svn_boolean_t clean_checkout; 211251881Speter 212251881Speter /* If this is a 'switch' operation, the new relpath of target_abspath, 213251881Speter else NULL. */ 214251881Speter const char *switch_relpath; 215251881Speter 216251881Speter /* The URL to the root of the repository. */ 217251881Speter const char *repos_root; 218251881Speter 219251881Speter /* The UUID of the repos, or NULL. */ 220251881Speter const char *repos_uuid; 221251881Speter 222251881Speter /* External diff3 to use for merges (can be null, in which case 223251881Speter internal merge code is used). */ 224251881Speter const char *diff3_cmd; 225251881Speter 226251881Speter /* Externals handler */ 227251881Speter svn_wc_external_update_t external_func; 228251881Speter void *external_baton; 229251881Speter 230251881Speter /* This editor sends back notifications as it edits. */ 231251881Speter svn_wc_notify_func2_t notify_func; 232251881Speter void *notify_baton; 233251881Speter 234251881Speter /* This editor is normally wrapped in a cancellation editor anyway, 235251881Speter so it doesn't bother to check for cancellation itself. However, 236251881Speter it needs a cancel_func and cancel_baton available to pass to 237251881Speter long-running functions. */ 238251881Speter svn_cancel_func_t cancel_func; 239251881Speter void *cancel_baton; 240251881Speter 241251881Speter /* This editor will invoke a interactive conflict-resolution 242251881Speter callback, if available. */ 243251881Speter svn_wc_conflict_resolver_func2_t conflict_func; 244251881Speter void *conflict_baton; 245251881Speter 246251881Speter /* Subtrees that were skipped during the edit, and therefore shouldn't 247251881Speter have their revision/url info updated at the end. If a path is a 248251881Speter directory, its descendants will also be skipped. The keys are paths 249251881Speter relative to the working copy root and the values unspecified. */ 250251881Speter apr_hash_t *skipped_trees; 251251881Speter 252251881Speter /* A mapping from const char * repos_relpaths to the apr_hash_t * instances 253251881Speter returned from fetch_dirents_func for that repos_relpath. These 254251881Speter are used to avoid issue #3569 in specific update scenarios where a 255251881Speter restricted depth is used. */ 256251881Speter apr_hash_t *dir_dirents; 257251881Speter 258251881Speter /* Absolute path of the working copy root or NULL if not initialized yet */ 259251881Speter const char *wcroot_abspath; 260251881Speter 261251881Speter apr_pool_t *pool; 262251881Speter}; 263251881Speter 264251881Speter 265251881Speter/* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being 266251881Speter * updated. 267251881Speter * 268251881Speter * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string 269251881Speter * LOCAL_ABSPATH. 270251881Speter */ 271251881Speterstatic svn_error_t * 272251881Speterremember_skipped_tree(struct edit_baton *eb, 273251881Speter const char *local_abspath, 274251881Speter apr_pool_t *scratch_pool) 275251881Speter{ 276251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 277251881Speter 278251881Speter svn_hash_sets(eb->skipped_trees, 279251881Speter apr_pstrdup(eb->pool, 280251881Speter svn_dirent_skip_ancestor(eb->wcroot_abspath, 281251881Speter local_abspath)), 282251881Speter (void *)1); 283251881Speter 284251881Speter return SVN_NO_ERROR; 285251881Speter} 286251881Speter 287251881Speter/* Per directory baton. Lives in its own subpool of the parent directory 288251881Speter or of the edit baton if there is no parent directory */ 289251881Speterstruct dir_baton 290251881Speter{ 291251881Speter /* Basename of this directory. */ 292251881Speter const char *name; 293251881Speter 294251881Speter /* Absolute path of this directory */ 295251881Speter const char *local_abspath; 296251881Speter 297251881Speter /* The repository relative path this directory will correspond to. */ 298251881Speter const char *new_relpath; 299251881Speter 300251881Speter /* The revision of the directory before updating */ 301251881Speter svn_revnum_t old_revision; 302251881Speter 303251881Speter /* The repos_relpath before updating/switching */ 304251881Speter const char *old_repos_relpath; 305251881Speter 306251881Speter /* The global edit baton. */ 307251881Speter struct edit_baton *edit_baton; 308251881Speter 309251881Speter /* Baton for this directory's parent, or NULL if this is the root 310251881Speter directory. */ 311251881Speter struct dir_baton *parent_baton; 312251881Speter 313251881Speter /* Set if updates to this directory are skipped */ 314251881Speter svn_boolean_t skip_this; 315251881Speter 316251881Speter /* Set if there was a previous notification for this directory */ 317251881Speter svn_boolean_t already_notified; 318251881Speter 319251881Speter /* Set if this directory is being added during this editor drive. */ 320251881Speter svn_boolean_t adding_dir; 321251881Speter 322251881Speter /* Set on a node and its descendants are not present in the working copy 323251881Speter but should still be updated (not skipped). These nodes should all be 324251881Speter marked as deleted. */ 325251881Speter svn_boolean_t shadowed; 326251881Speter 327251881Speter /* Set on a node when the existing node is obstructed, and the edit operation 328251881Speter continues as semi-shadowed update */ 329251881Speter svn_boolean_t edit_obstructed; 330251881Speter 331251881Speter /* The (new) changed_* information, cached to avoid retrieving it later */ 332251881Speter svn_revnum_t changed_rev; 333251881Speter apr_time_t changed_date; 334251881Speter const char *changed_author; 335251881Speter 336251881Speter /* If not NULL, contains a mapping of const char* basenames of children that 337251881Speter have been deleted to their svn_skel_t* tree conflicts. 338251881Speter We store this hash to allow replacements to continue under a just 339251881Speter installed tree conflict. 340251881Speter 341251881Speter The add after the delete will then update the tree conflicts information 342251881Speter and reinstall it. */ 343251881Speter apr_hash_t *deletion_conflicts; 344251881Speter 345251881Speter /* A hash of file names (only the hash key matters) seen by add_file 346251881Speter and not yet added to the database by close_file. */ 347251881Speter apr_hash_t *not_present_files; 348251881Speter 349251881Speter /* Set if an unversioned dir of the same name already existed in 350251881Speter this directory. */ 351251881Speter svn_boolean_t obstruction_found; 352251881Speter 353251881Speter /* Set if a dir of the same name already exists and is 354251881Speter scheduled for addition without history. */ 355251881Speter svn_boolean_t add_existed; 356251881Speter 357251881Speter /* An array of svn_prop_t structures, representing all the property 358251881Speter changes to be applied to this directory. */ 359251881Speter apr_array_header_t *propchanges; 360251881Speter 361251881Speter /* A boolean indicating whether this node or one of its children has 362251881Speter received any 'real' changes. Used to avoid tree conflicts for simple 363251881Speter entryprop changes, like lock management */ 364251881Speter svn_boolean_t edited; 365251881Speter 366251881Speter /* The tree conflict to install once the node is really edited */ 367251881Speter svn_skel_t *edit_conflict; 368251881Speter 369251881Speter /* The bump information for this directory. */ 370251881Speter struct bump_dir_info *bump_info; 371251881Speter 372251881Speter /* The depth of the directory in the wc (or inferred if added). Not 373251881Speter used for filtering; we have a separate wrapping editor for that. */ 374251881Speter svn_depth_t ambient_depth; 375251881Speter 376251881Speter /* Was the directory marked as incomplete before the update? 377251881Speter (In other words, are we resuming an interrupted update?) 378251881Speter 379251881Speter If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes 380251881Speter and properties for/of the directory. If WAS_INCOMPLETE is FALSE then 381251881Speter we only receive the changes in/for children and properties.*/ 382251881Speter svn_boolean_t was_incomplete; 383251881Speter 384251881Speter /* The pool in which this baton itself is allocated. */ 385251881Speter apr_pool_t *pool; 386251881Speter 387251881Speter /* how many nodes are referring to baton? */ 388251881Speter int ref_count; 389251881Speter 390251881Speter}; 391251881Speter 392251881Speter 393251881Speterstruct handler_baton 394251881Speter{ 395251881Speter svn_txdelta_window_handler_t apply_handler; 396251881Speter void *apply_baton; 397251881Speter apr_pool_t *pool; 398251881Speter struct file_baton *fb; 399251881Speter 400251881Speter /* Where we are assembling the new file. */ 401251881Speter const char *new_text_base_tmp_abspath; 402251881Speter 403251881Speter /* The expected source checksum of the text source or NULL if no base 404251881Speter checksum is available (MD5 if the server provides a checksum, SHA1 if 405251881Speter the server doesn't) */ 406251881Speter svn_checksum_t *expected_source_checksum; 407251881Speter 408251881Speter /* Why two checksums? 409251881Speter The editor currently provides an md5 which we use to detect corruption 410251881Speter during transmission. We use the sha1 inside libsvn_wc both for pristine 411251881Speter handling and corruption detection. In the future, the editor will also 412251881Speter provide a sha1, so we may not have to calculate both, but for the time 413251881Speter being, that's the way it is. */ 414251881Speter 415251881Speter /* The calculated checksum of the text source or NULL if the acual 416251881Speter checksum is not being calculated. The checksum kind is identical to the 417251881Speter kind of expected_source_checksum. */ 418251881Speter svn_checksum_t *actual_source_checksum; 419251881Speter 420251881Speter /* The stream used to calculate the source checksums */ 421251881Speter svn_stream_t *source_checksum_stream; 422251881Speter 423251881Speter /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH. 424251881Speter This is initialized to all zeroes when the baton is created, then 425251881Speter populated with the MD5 digest of the resultant fulltext after the 426251881Speter last window is handled by the handler returned from 427251881Speter apply_textdelta(). */ 428251881Speter unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE]; 429251881Speter 430251881Speter /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for 431251881Speter eventually writing the pristine. */ 432251881Speter svn_checksum_t * new_text_base_sha1_checksum; 433251881Speter}; 434251881Speter 435251881Speter 436251881Speter/* Get an empty file in the temporary area for WRI_ABSPATH. The file will 437251881Speter not be set for automatic deletion, and the name will be returned in 438251881Speter TMP_FILENAME. 439251881Speter 440251881Speter This implementation creates a new empty file with a unique name. 441251881Speter 442251881Speter ### This is inefficient for callers that just want an empty file to read 443251881Speter ### from. There could be (and there used to be) a permanent, shared 444251881Speter ### empty file for this purpose. 445251881Speter 446251881Speter ### This is inefficient for callers that just want to reserve a unique 447251881Speter ### file name to create later. A better way may not be readily available. 448251881Speter */ 449251881Speterstatic svn_error_t * 450251881Speterget_empty_tmp_file(const char **tmp_filename, 451251881Speter svn_wc__db_t *db, 452251881Speter const char *wri_abspath, 453251881Speter apr_pool_t *result_pool, 454251881Speter apr_pool_t *scratch_pool) 455251881Speter{ 456251881Speter const char *temp_dir_abspath; 457251881Speter 458251881Speter SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath, 459251881Speter scratch_pool, scratch_pool)); 460251881Speter SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath, 461251881Speter svn_io_file_del_none, 462251881Speter scratch_pool, scratch_pool)); 463251881Speter 464251881Speter return SVN_NO_ERROR; 465251881Speter} 466251881Speter 467251881Speter/* An APR pool cleanup handler. This runs the working queue for an 468251881Speter editor baton. */ 469251881Speterstatic apr_status_t 470251881Spetercleanup_edit_baton(void *edit_baton) 471251881Speter{ 472251881Speter struct edit_baton *eb = edit_baton; 473251881Speter svn_error_t *err; 474251881Speter apr_pool_t *pool = apr_pool_parent_get(eb->pool); 475251881Speter 476251881Speter err = svn_wc__wq_run(eb->db, eb->wcroot_abspath, 477251881Speter NULL /* cancel_func */, NULL /* cancel_baton */, 478251881Speter pool); 479251881Speter 480251881Speter if (err) 481251881Speter { 482251881Speter apr_status_t apr_err = err->apr_err; 483251881Speter svn_error_clear(err); 484251881Speter return apr_err; 485251881Speter } 486251881Speter return APR_SUCCESS; 487251881Speter} 488251881Speter 489251881Speter/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton. 490251881Speter If PATH and PB are NULL, this is the root directory of the edit; in this 491251881Speter case, make the new dir baton in a subpool of EB->pool. 492251881Speter ADDING should be TRUE if we are adding this directory. */ 493251881Speterstatic svn_error_t * 494251881Spetermake_dir_baton(struct dir_baton **d_p, 495251881Speter const char *path, 496251881Speter struct edit_baton *eb, 497251881Speter struct dir_baton *pb, 498251881Speter svn_boolean_t adding, 499251881Speter apr_pool_t *scratch_pool) 500251881Speter{ 501251881Speter apr_pool_t *dir_pool; 502251881Speter struct dir_baton *d; 503251881Speter 504251881Speter if (pb != NULL) 505251881Speter dir_pool = svn_pool_create(pb->pool); 506251881Speter else 507251881Speter dir_pool = svn_pool_create(eb->pool); 508251881Speter 509251881Speter SVN_ERR_ASSERT(path || (! pb)); 510251881Speter 511251881Speter /* Okay, no easy out, so allocate and initialize a dir baton. */ 512251881Speter d = apr_pcalloc(dir_pool, sizeof(*d)); 513251881Speter 514251881Speter /* Construct the PATH and baseNAME of this directory. */ 515251881Speter if (path) 516251881Speter { 517251881Speter d->name = svn_dirent_basename(path, dir_pool); 518251881Speter SVN_ERR(path_join_under_root(&d->local_abspath, 519251881Speter pb->local_abspath, d->name, dir_pool)); 520251881Speter } 521251881Speter else 522251881Speter { 523251881Speter /* This is the root baton. */ 524251881Speter d->name = NULL; 525251881Speter d->local_abspath = eb->anchor_abspath; 526251881Speter } 527251881Speter 528251881Speter /* Figure out the new_relpath for this directory. */ 529251881Speter if (eb->switch_relpath) 530251881Speter { 531251881Speter /* Handle switches... */ 532251881Speter 533251881Speter if (pb == NULL) 534251881Speter { 535251881Speter if (*eb->target_basename == '\0') 536251881Speter { 537251881Speter /* No parent baton and target_basename=="" means that we are 538251881Speter the target of the switch. Thus, our NEW_RELPATH will be 539251881Speter the SWITCH_RELPATH. */ 540251881Speter d->new_relpath = eb->switch_relpath; 541251881Speter } 542251881Speter else 543251881Speter { 544251881Speter /* This node is NOT the target of the switch (one of our 545251881Speter children is the target); therefore, it must already exist. 546251881Speter Get its old REPOS_RELPATH, as it won't be changing. */ 547251881Speter SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL, 548251881Speter eb->db, d->local_abspath, 549251881Speter dir_pool, scratch_pool)); 550251881Speter } 551251881Speter } 552251881Speter else 553251881Speter { 554251881Speter /* This directory is *not* the root (has a parent). If there is 555251881Speter no grandparent, then we may have anchored at the parent, 556251881Speter and self is the target. If we match the target, then set 557251881Speter NEW_RELPATH to the SWITCH_RELPATH. 558251881Speter 559251881Speter Otherwise, we simply extend NEW_RELPATH from the parent. */ 560251881Speter if (pb->parent_baton == NULL 561251881Speter && strcmp(eb->target_basename, d->name) == 0) 562251881Speter d->new_relpath = eb->switch_relpath; 563251881Speter else 564251881Speter d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, 565251881Speter dir_pool); 566251881Speter } 567251881Speter } 568251881Speter else /* must be an update */ 569251881Speter { 570251881Speter /* If we are adding the node, then simply extend the parent's 571251881Speter relpath for our own. */ 572251881Speter if (adding) 573251881Speter { 574251881Speter SVN_ERR_ASSERT(pb != NULL); 575251881Speter d->new_relpath = svn_relpath_join(pb->new_relpath, d->name, 576251881Speter dir_pool); 577251881Speter } 578251881Speter else 579251881Speter { 580251881Speter SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL, 581251881Speter eb->db, d->local_abspath, 582251881Speter dir_pool, scratch_pool)); 583251881Speter SVN_ERR_ASSERT(d->new_relpath); 584251881Speter } 585251881Speter } 586251881Speter 587251881Speter d->edit_baton = eb; 588251881Speter d->parent_baton = pb; 589251881Speter d->pool = dir_pool; 590251881Speter d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t)); 591251881Speter d->obstruction_found = FALSE; 592251881Speter d->add_existed = FALSE; 593251881Speter d->ref_count = 1; 594251881Speter d->old_revision = SVN_INVALID_REVNUM; 595251881Speter d->adding_dir = adding; 596251881Speter d->changed_rev = SVN_INVALID_REVNUM; 597251881Speter d->not_present_files = apr_hash_make(dir_pool); 598251881Speter 599251881Speter /* Copy some flags from the parent baton */ 600251881Speter if (pb) 601251881Speter { 602251881Speter d->skip_this = pb->skip_this; 603251881Speter d->shadowed = pb->shadowed || pb->edit_obstructed; 604251881Speter 605251881Speter /* the parent's bump info has one more referer */ 606251881Speter pb->ref_count++; 607251881Speter } 608251881Speter 609251881Speter /* The caller of this function needs to fill these in. */ 610251881Speter d->ambient_depth = svn_depth_unknown; 611251881Speter d->was_incomplete = FALSE; 612251881Speter 613251881Speter *d_p = d; 614251881Speter return SVN_NO_ERROR; 615251881Speter} 616251881Speter 617251881Speter 618251881Speter/* Forward declarations. */ 619251881Speterstatic svn_error_t * 620251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted, 621251881Speter svn_boolean_t *ignored, 622251881Speter svn_wc__db_t *db, 623251881Speter const char *local_abspath, 624251881Speter apr_pool_t *scratch_pool); 625251881Speter 626251881Speter 627251881Speterstatic void 628251881Speterdo_notification(const struct edit_baton *eb, 629251881Speter const char *local_abspath, 630251881Speter svn_node_kind_t kind, 631251881Speter svn_wc_notify_action_t action, 632251881Speter apr_pool_t *scratch_pool) 633251881Speter{ 634251881Speter svn_wc_notify_t *notify; 635251881Speter 636251881Speter if (eb->notify_func == NULL) 637251881Speter return; 638251881Speter 639251881Speter notify = svn_wc_create_notify(local_abspath, action, scratch_pool); 640251881Speter notify->kind = kind; 641251881Speter 642251881Speter (*eb->notify_func)(eb->notify_baton, notify, scratch_pool); 643251881Speter} 644251881Speter 645251881Speter/* Decrement the directory's reference count. If it hits zero, 646251881Speter then this directory is "done". This means it is safe to clear its pool. 647251881Speter 648251881Speter In addition, when the directory is "done", we recurse to possible cleanup 649251881Speter the parent directory. 650251881Speter*/ 651251881Speterstatic svn_error_t * 652251881Spetermaybe_release_dir_info(struct dir_baton *db) 653251881Speter{ 654251881Speter db->ref_count--; 655251881Speter 656251881Speter if (!db->ref_count) 657251881Speter { 658251881Speter struct dir_baton *pb = db->parent_baton; 659251881Speter 660251881Speter svn_pool_destroy(db->pool); 661251881Speter 662251881Speter if (pb) 663251881Speter SVN_ERR(maybe_release_dir_info(pb)); 664251881Speter } 665251881Speter 666251881Speter return SVN_NO_ERROR; 667251881Speter} 668251881Speter 669251881Speter/* Per file baton. Lives in its own subpool below the pool of the parent 670251881Speter directory */ 671251881Speterstruct file_baton 672251881Speter{ 673251881Speter /* Pool specific to this file_baton. */ 674251881Speter apr_pool_t *pool; 675251881Speter 676251881Speter /* Name of this file (its entry in the directory). */ 677251881Speter const char *name; 678251881Speter 679251881Speter /* Absolute path to this file */ 680251881Speter const char *local_abspath; 681251881Speter 682251881Speter /* The repository relative path this file will correspond to. */ 683251881Speter const char *new_relpath; 684251881Speter 685251881Speter /* The revision of the file before updating */ 686251881Speter svn_revnum_t old_revision; 687251881Speter 688251881Speter /* The repos_relpath before updating/switching */ 689251881Speter const char *old_repos_relpath; 690251881Speter 691251881Speter /* The global edit baton. */ 692251881Speter struct edit_baton *edit_baton; 693251881Speter 694251881Speter /* The parent directory of this file. */ 695251881Speter struct dir_baton *dir_baton; 696251881Speter 697251881Speter /* Set if updates to this directory are skipped */ 698251881Speter svn_boolean_t skip_this; 699251881Speter 700251881Speter /* Set if there was a previous notification */ 701251881Speter svn_boolean_t already_notified; 702251881Speter 703251881Speter /* Set if this file is new. */ 704251881Speter svn_boolean_t adding_file; 705251881Speter 706251881Speter /* Set if an unversioned file of the same name already existed in 707251881Speter this directory. */ 708251881Speter svn_boolean_t obstruction_found; 709251881Speter 710251881Speter /* Set if a file of the same name already exists and is 711251881Speter scheduled for addition without history. */ 712251881Speter svn_boolean_t add_existed; 713251881Speter 714251881Speter /* Set if this file is being added in the BASE layer, but is not-present 715251881Speter in the working copy (replaced, deleted, etc.). */ 716251881Speter svn_boolean_t shadowed; 717251881Speter 718251881Speter /* Set on a node when the existing node is obstructed, and the edit operation 719251881Speter continues as semi-shadowed update */ 720251881Speter svn_boolean_t edit_obstructed; 721251881Speter 722251881Speter /* The (new) changed_* information, cached to avoid retrieving it later */ 723251881Speter svn_revnum_t changed_rev; 724251881Speter apr_time_t changed_date; 725251881Speter const char *changed_author; 726251881Speter 727251881Speter /* If there are file content changes, these are the checksums of the 728251881Speter resulting new text base, which is in the pristine store, else NULL. */ 729251881Speter const svn_checksum_t *new_text_base_md5_checksum; 730251881Speter const svn_checksum_t *new_text_base_sha1_checksum; 731251881Speter 732251881Speter /* The checksum of the file before the update */ 733251881Speter const svn_checksum_t *original_checksum; 734251881Speter 735251881Speter /* An array of svn_prop_t structures, representing all the property 736251881Speter changes to be applied to this file. Once a file baton is 737251881Speter initialized, this is never NULL, but it may have zero elements. */ 738251881Speter apr_array_header_t *propchanges; 739251881Speter 740251881Speter /* For existing files, whether there are local modifications. FALSE for added 741251881Speter files */ 742251881Speter svn_boolean_t local_prop_mods; 743251881Speter 744251881Speter /* Bump information for the directory this file lives in */ 745251881Speter struct bump_dir_info *bump_info; 746251881Speter 747251881Speter /* A boolean indicating whether this node or one of its children has 748251881Speter received any 'real' changes. Used to avoid tree conflicts for simple 749251881Speter entryprop changes, like lock management */ 750251881Speter svn_boolean_t edited; 751251881Speter 752251881Speter /* The tree conflict to install once the node is really edited */ 753251881Speter svn_skel_t *edit_conflict; 754251881Speter}; 755251881Speter 756251881Speter 757251881Speter/* Make a new file baton in a subpool of PB->pool. PB is the parent baton. 758251881Speter * PATH is relative to the root of the edit. ADDING tells whether this file 759251881Speter * is being added. */ 760251881Speterstatic svn_error_t * 761251881Spetermake_file_baton(struct file_baton **f_p, 762251881Speter struct dir_baton *pb, 763251881Speter const char *path, 764251881Speter svn_boolean_t adding, 765251881Speter apr_pool_t *scratch_pool) 766251881Speter{ 767251881Speter struct edit_baton *eb = pb->edit_baton; 768251881Speter apr_pool_t *file_pool = svn_pool_create(pb->pool); 769251881Speter struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f)); 770251881Speter 771251881Speter SVN_ERR_ASSERT(path); 772251881Speter 773251881Speter /* Make the file's on-disk name. */ 774251881Speter f->name = svn_dirent_basename(path, file_pool); 775251881Speter f->old_revision = SVN_INVALID_REVNUM; 776251881Speter SVN_ERR(path_join_under_root(&f->local_abspath, 777251881Speter pb->local_abspath, f->name, file_pool)); 778251881Speter 779251881Speter /* Figure out the new URL for this file. */ 780251881Speter if (eb->switch_relpath) 781251881Speter { 782251881Speter /* Handle switches... */ 783251881Speter 784251881Speter /* This file has a parent directory. If there is 785251881Speter no grandparent, then we may have anchored at the parent, 786251881Speter and self is the target. If we match the target, then set 787251881Speter NEW_RELPATH to the SWITCH_RELPATH. 788251881Speter 789251881Speter Otherwise, we simply extend NEW_RELPATH from the parent. */ 790251881Speter if (pb->parent_baton == NULL 791251881Speter && strcmp(eb->target_basename, f->name) == 0) 792251881Speter f->new_relpath = eb->switch_relpath; 793251881Speter else 794251881Speter f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, 795251881Speter file_pool); 796251881Speter } 797251881Speter else /* must be an update */ 798251881Speter { 799251881Speter if (adding) 800251881Speter f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool); 801251881Speter else 802251881Speter { 803251881Speter SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL, 804251881Speter eb->db, f->local_abspath, 805251881Speter file_pool, scratch_pool)); 806251881Speter SVN_ERR_ASSERT(f->new_relpath); 807251881Speter } 808251881Speter } 809251881Speter 810251881Speter f->pool = file_pool; 811251881Speter f->edit_baton = pb->edit_baton; 812251881Speter f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t)); 813251881Speter f->bump_info = pb->bump_info; 814251881Speter f->adding_file = adding; 815251881Speter f->obstruction_found = FALSE; 816251881Speter f->add_existed = FALSE; 817251881Speter f->skip_this = pb->skip_this; 818251881Speter f->shadowed = pb->shadowed || pb->edit_obstructed; 819251881Speter f->dir_baton = pb; 820251881Speter f->changed_rev = SVN_INVALID_REVNUM; 821251881Speter 822251881Speter /* the directory has one more referer now */ 823251881Speter pb->ref_count++; 824251881Speter 825251881Speter *f_p = f; 826251881Speter return SVN_NO_ERROR; 827251881Speter} 828251881Speter 829251881Speter/* Complete a conflict skel by describing the update. 830251881Speter * 831251881Speter * LOCAL_KIND is the node kind of the tree conflict victim in the 832251881Speter * working copy. 833251881Speter * 834251881Speter * All temporary allocations are be made in SCRATCH_POOL, while allocations 835251881Speter * needed for the returned conflict struct are made in RESULT_POOL. 836251881Speter */ 837251881Speterstatic svn_error_t * 838251881Spetercomplete_conflict(svn_skel_t *conflict, 839251881Speter const struct edit_baton *eb, 840251881Speter const char *local_abspath, 841251881Speter const char *old_repos_relpath, 842251881Speter svn_revnum_t old_revision, 843251881Speter const char *new_repos_relpath, 844251881Speter svn_node_kind_t local_kind, 845251881Speter svn_node_kind_t target_kind, 846251881Speter apr_pool_t *result_pool, 847251881Speter apr_pool_t *scratch_pool) 848251881Speter{ 849251881Speter svn_wc_conflict_version_t *original_version; 850251881Speter svn_wc_conflict_version_t *target_version; 851251881Speter svn_boolean_t is_complete; 852251881Speter 853251881Speter if (!conflict) 854251881Speter return SVN_NO_ERROR; /* Not conflicted */ 855251881Speter 856251881Speter SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict)); 857251881Speter 858251881Speter if (is_complete) 859251881Speter return SVN_NO_ERROR; /* Already completed */ 860251881Speter 861251881Speter if (old_repos_relpath) 862251881Speter original_version = svn_wc_conflict_version_create2(eb->repos_root, 863251881Speter eb->repos_uuid, 864251881Speter old_repos_relpath, 865251881Speter old_revision, 866251881Speter local_kind, 867251881Speter result_pool); 868251881Speter else 869251881Speter original_version = NULL; 870251881Speter 871251881Speter if (new_repos_relpath) 872251881Speter target_version = svn_wc_conflict_version_create2(eb->repos_root, 873251881Speter eb->repos_uuid, 874251881Speter new_repos_relpath, 875251881Speter *eb->target_revision, 876251881Speter target_kind, 877251881Speter result_pool); 878251881Speter else 879251881Speter target_version = NULL; 880251881Speter 881251881Speter if (eb->switch_relpath) 882251881Speter SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict, 883251881Speter original_version, 884251881Speter target_version, 885251881Speter result_pool, scratch_pool)); 886251881Speter else 887251881Speter SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict, 888251881Speter original_version, 889251881Speter target_version, 890251881Speter result_pool, scratch_pool)); 891251881Speter 892251881Speter return SVN_NO_ERROR; 893251881Speter} 894251881Speter 895251881Speter 896251881Speter/* Called when a directory is really edited, to avoid marking a 897251881Speter tree conflict on a node for a no-change edit */ 898251881Speterstatic svn_error_t * 899251881Spetermark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool) 900251881Speter{ 901251881Speter if (db->edited) 902251881Speter return SVN_NO_ERROR; 903251881Speter 904251881Speter if (db->parent_baton) 905251881Speter SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool)); 906251881Speter 907251881Speter db->edited = TRUE; 908251881Speter 909251881Speter if (db->edit_conflict) 910251881Speter { 911251881Speter /* We have a (delayed) tree conflict to install */ 912251881Speter 913251881Speter SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton, 914251881Speter db->local_abspath, 915251881Speter db->old_repos_relpath, db->old_revision, 916251881Speter db->new_relpath, 917251881Speter svn_node_dir, svn_node_dir, 918251881Speter db->pool, scratch_pool)); 919251881Speter SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db, 920251881Speter db->local_abspath, 921251881Speter db->edit_conflict, NULL, 922251881Speter scratch_pool)); 923251881Speter 924251881Speter do_notification(db->edit_baton, db->local_abspath, svn_node_dir, 925251881Speter svn_wc_notify_tree_conflict, scratch_pool); 926251881Speter db->already_notified = TRUE; 927253734Speter 928251881Speter } 929251881Speter 930251881Speter return SVN_NO_ERROR; 931251881Speter} 932251881Speter 933251881Speter/* Called when a file is really edited, to avoid marking a 934251881Speter tree conflict on a node for a no-change edit */ 935251881Speterstatic svn_error_t * 936251881Spetermark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool) 937251881Speter{ 938251881Speter if (fb->edited) 939251881Speter return SVN_NO_ERROR; 940251881Speter 941251881Speter SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool)); 942251881Speter 943251881Speter fb->edited = TRUE; 944251881Speter 945251881Speter if (fb->edit_conflict) 946251881Speter { 947251881Speter /* We have a (delayed) tree conflict to install */ 948251881Speter 949251881Speter SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, 950251881Speter fb->local_abspath, fb->old_repos_relpath, 951251881Speter fb->old_revision, fb->new_relpath, 952251881Speter svn_node_file, svn_node_file, 953251881Speter fb->pool, scratch_pool)); 954251881Speter 955251881Speter SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db, 956251881Speter fb->local_abspath, 957251881Speter fb->edit_conflict, NULL, 958251881Speter scratch_pool)); 959251881Speter 960251881Speter do_notification(fb->edit_baton, fb->local_abspath, svn_node_file, 961251881Speter svn_wc_notify_tree_conflict, scratch_pool); 962251881Speter fb->already_notified = TRUE; 963251881Speter } 964251881Speter 965251881Speter return SVN_NO_ERROR; 966251881Speter} 967251881Speter 968251881Speter 969251881Speter/* Handle the next delta window of the file described by BATON. If it is 970251881Speter * the end (WINDOW == NULL), then check the checksum, store the text in the 971251881Speter * pristine store and write its details into BATON->fb->new_text_base_*. */ 972251881Speterstatic svn_error_t * 973251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton) 974251881Speter{ 975251881Speter struct handler_baton *hb = baton; 976251881Speter struct file_baton *fb = hb->fb; 977251881Speter svn_wc__db_t *db = fb->edit_baton->db; 978251881Speter svn_error_t *err; 979251881Speter 980251881Speter /* Apply this window. We may be done at that point. */ 981251881Speter err = hb->apply_handler(window, hb->apply_baton); 982251881Speter if (window != NULL && !err) 983251881Speter return SVN_NO_ERROR; 984251881Speter 985251881Speter if (hb->expected_source_checksum) 986251881Speter { 987251881Speter /* Close the stream to calculate HB->actual_source_md5_checksum. */ 988251881Speter svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream); 989251881Speter 990251881Speter if (!err2) 991251881Speter { 992251881Speter SVN_ERR_ASSERT(hb->expected_source_checksum->kind == 993251881Speter hb->actual_source_checksum->kind); 994251881Speter 995251881Speter if (!svn_checksum_match(hb->expected_source_checksum, 996251881Speter hb->actual_source_checksum)) 997251881Speter { 998251881Speter err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err, 999251881Speter _("Checksum mismatch while updating '%s':\n" 1000251881Speter " expected: %s\n" 1001251881Speter " actual: %s\n"), 1002251881Speter svn_dirent_local_style(fb->local_abspath, hb->pool), 1003251881Speter svn_checksum_to_cstring(hb->expected_source_checksum, 1004251881Speter hb->pool), 1005251881Speter svn_checksum_to_cstring(hb->actual_source_checksum, 1006251881Speter hb->pool)); 1007251881Speter } 1008251881Speter } 1009251881Speter 1010251881Speter err = svn_error_compose_create(err, err2); 1011251881Speter } 1012251881Speter 1013251881Speter if (err) 1014251881Speter { 1015251881Speter /* We failed to apply the delta; clean up the temporary file. */ 1016251881Speter svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, TRUE, 1017251881Speter hb->pool)); 1018251881Speter } 1019251881Speter else 1020251881Speter { 1021251881Speter /* Tell the file baton about the new text base's checksums. */ 1022251881Speter fb->new_text_base_md5_checksum = 1023251881Speter svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool); 1024251881Speter fb->new_text_base_sha1_checksum = 1025251881Speter svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool); 1026251881Speter 1027251881Speter /* Store the new pristine text in the pristine store now. Later, in a 1028251881Speter single transaction we will update the BASE_NODE to include a 1029251881Speter reference to this pristine text's checksum. */ 1030251881Speter SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath, 1031251881Speter fb->new_text_base_sha1_checksum, 1032251881Speter fb->new_text_base_md5_checksum, 1033251881Speter hb->pool)); 1034251881Speter } 1035251881Speter 1036251881Speter svn_pool_destroy(hb->pool); 1037251881Speter 1038251881Speter return err; 1039251881Speter} 1040251881Speter 1041251881Speter 1042251881Speter/* Find the last-change info within ENTRY_PROPS, and return then in the 1043251881Speter CHANGED_* parameters. Each parameter will be initialized to its "none" 1044251881Speter value, and will contain the relavent info if found. 1045251881Speter 1046251881Speter CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be 1047251881Speter used for some temporary allocations. 1048251881Speter*/ 1049251881Speterstatic svn_error_t * 1050251881Speteraccumulate_last_change(svn_revnum_t *changed_rev, 1051251881Speter apr_time_t *changed_date, 1052251881Speter const char **changed_author, 1053251881Speter const apr_array_header_t *entry_props, 1054251881Speter apr_pool_t *result_pool, 1055251881Speter apr_pool_t *scratch_pool) 1056251881Speter{ 1057251881Speter int i; 1058251881Speter 1059251881Speter *changed_rev = SVN_INVALID_REVNUM; 1060251881Speter *changed_date = 0; 1061251881Speter *changed_author = NULL; 1062251881Speter 1063251881Speter for (i = 0; i < entry_props->nelts; ++i) 1064251881Speter { 1065251881Speter const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t); 1066251881Speter 1067251881Speter /* A prop value of NULL means the information was not 1068251881Speter available. We don't remove this field from the entries 1069251881Speter file; we have convention just leave it empty. So let's 1070251881Speter just skip those entry props that have no values. */ 1071251881Speter if (! prop->value) 1072251881Speter continue; 1073251881Speter 1074251881Speter if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR)) 1075251881Speter *changed_author = apr_pstrdup(result_pool, prop->value->data); 1076251881Speter else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV)) 1077251881Speter { 1078251881Speter apr_int64_t rev; 1079251881Speter SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data)); 1080251881Speter *changed_rev = (svn_revnum_t)rev; 1081251881Speter } 1082251881Speter else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE)) 1083251881Speter SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data, 1084251881Speter scratch_pool)); 1085251881Speter 1086251881Speter /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID 1087251881Speter property here. */ 1088251881Speter } 1089251881Speter 1090251881Speter return SVN_NO_ERROR; 1091251881Speter} 1092251881Speter 1093251881Speter 1094251881Speter/* Join ADD_PATH to BASE_PATH. If ADD_PATH is absolute, or if any ".." 1095251881Speter * component of it resolves to a path above BASE_PATH, then return 1096251881Speter * SVN_ERR_WC_OBSTRUCTED_UPDATE. 1097251881Speter * 1098251881Speter * This is to prevent the situation where the repository contains, 1099251881Speter * say, "..\nastyfile". Although that's perfectly legal on some 1100251881Speter * systems, when checked out onto Win32 it would cause "nastyfile" to 1101251881Speter * be created in the parent of the current edit directory. 1102251881Speter * 1103251881Speter * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846) 1104251881Speter */ 1105251881Speterstatic svn_error_t * 1106251881Speterpath_join_under_root(const char **result_path, 1107251881Speter const char *base_path, 1108251881Speter const char *add_path, 1109251881Speter apr_pool_t *pool) 1110251881Speter{ 1111251881Speter svn_boolean_t under_root; 1112251881Speter 1113251881Speter SVN_ERR(svn_dirent_is_under_root(&under_root, 1114251881Speter result_path, base_path, add_path, pool)); 1115251881Speter 1116251881Speter if (! under_root) 1117251881Speter { 1118251881Speter return svn_error_createf( 1119251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 1120251881Speter _("Path '%s' is not in the working copy"), 1121251881Speter svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool), 1122251881Speter pool)); 1123251881Speter } 1124251881Speter 1125251881Speter /* This catches issue #3288 */ 1126251881Speter if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0) 1127251881Speter { 1128251881Speter return svn_error_createf( 1129251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 1130251881Speter _("'%s' is not valid as filename in directory '%s'"), 1131251881Speter svn_dirent_local_style(add_path, pool), 1132251881Speter svn_dirent_local_style(base_path, pool)); 1133251881Speter } 1134251881Speter 1135251881Speter return SVN_NO_ERROR; 1136251881Speter} 1137251881Speter 1138251881Speter 1139251881Speter/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/ 1140251881Speter 1141251881Speter/* An svn_delta_editor_t function. */ 1142251881Speterstatic svn_error_t * 1143251881Speterset_target_revision(void *edit_baton, 1144251881Speter svn_revnum_t target_revision, 1145251881Speter apr_pool_t *pool) 1146251881Speter{ 1147251881Speter struct edit_baton *eb = edit_baton; 1148251881Speter 1149251881Speter *(eb->target_revision) = target_revision; 1150251881Speter return SVN_NO_ERROR; 1151251881Speter} 1152251881Speter 1153251881Speterstatic svn_error_t * 1154251881Spetercheck_tree_conflict(svn_skel_t **pconflict, 1155251881Speter struct edit_baton *eb, 1156251881Speter const char *local_abspath, 1157251881Speter svn_wc__db_status_t working_status, 1158251881Speter svn_boolean_t exists_in_repos, 1159251881Speter svn_node_kind_t expected_kind, 1160251881Speter svn_wc_conflict_action_t action, 1161251881Speter apr_pool_t *result_pool, 1162251881Speter apr_pool_t *scratch_pool); 1163251881Speter 1164251881Speter/* An svn_delta_editor_t function. */ 1165251881Speterstatic svn_error_t * 1166251881Speteropen_root(void *edit_baton, 1167251881Speter svn_revnum_t base_revision, /* This is ignored in co */ 1168251881Speter apr_pool_t *pool, 1169251881Speter void **dir_baton) 1170251881Speter{ 1171251881Speter struct edit_baton *eb = edit_baton; 1172251881Speter struct dir_baton *db; 1173251881Speter svn_boolean_t already_conflicted, conflict_ignored; 1174251881Speter svn_error_t *err; 1175251881Speter svn_wc__db_status_t status; 1176251881Speter svn_wc__db_status_t base_status; 1177251881Speter svn_node_kind_t kind; 1178251881Speter svn_boolean_t have_work; 1179251881Speter 1180251881Speter /* Note that something interesting is actually happening in this 1181251881Speter edit run. */ 1182251881Speter eb->root_opened = TRUE; 1183251881Speter 1184251881Speter SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool)); 1185251881Speter *dir_baton = db; 1186251881Speter 1187251881Speter err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored, 1188251881Speter eb->db, db->local_abspath, pool); 1189251881Speter 1190251881Speter if (err) 1191251881Speter { 1192251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1193251881Speter return svn_error_trace(err); 1194251881Speter 1195251881Speter svn_error_clear(err); 1196251881Speter already_conflicted = conflict_ignored = FALSE; 1197251881Speter } 1198251881Speter else if (already_conflicted) 1199251881Speter { 1200251881Speter /* Record a skip of both the anchor and target in the skipped tree 1201251881Speter as the anchor itself might not be updated */ 1202251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 1203251881Speter SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool)); 1204251881Speter 1205251881Speter db->skip_this = TRUE; 1206251881Speter db->already_notified = TRUE; 1207251881Speter 1208251881Speter /* Notify that we skipped the target, while we actually skipped 1209251881Speter the anchor */ 1210251881Speter do_notification(eb, eb->target_abspath, svn_node_unknown, 1211251881Speter svn_wc_notify_skip_conflicted, pool); 1212251881Speter 1213251881Speter return SVN_NO_ERROR; 1214251881Speter } 1215251881Speter 1216251881Speter 1217251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision, 1218251881Speter &db->old_repos_relpath, NULL, NULL, 1219251881Speter &db->changed_rev, &db->changed_date, 1220251881Speter &db->changed_author, &db->ambient_depth, 1221251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1222251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1223251881Speter NULL, NULL, &have_work, 1224251881Speter eb->db, db->local_abspath, 1225251881Speter db->pool, pool)); 1226251881Speter 1227251881Speter if (conflict_ignored) 1228251881Speter { 1229251881Speter db->shadowed = TRUE; 1230251881Speter } 1231251881Speter else if (have_work) 1232251881Speter { 1233251881Speter const char *move_src_root_abspath; 1234251881Speter 1235251881Speter SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath, 1236251881Speter NULL, eb->db, db->local_abspath, 1237251881Speter pool, pool)); 1238251881Speter if (move_src_root_abspath || *eb->target_basename == '\0') 1239251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, 1240251881Speter &db->old_revision, 1241251881Speter &db->old_repos_relpath, NULL, NULL, 1242251881Speter &db->changed_rev, &db->changed_date, 1243251881Speter &db->changed_author, 1244251881Speter &db->ambient_depth, 1245251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 1246251881Speter eb->db, db->local_abspath, 1247251881Speter db->pool, pool)); 1248251881Speter 1249251881Speter if (move_src_root_abspath) 1250251881Speter { 1251251881Speter /* This is an update anchored inside a move. We need to 1252251881Speter raise a move-edit tree-conflict on the move root to 1253251881Speter update the move destination. */ 1254251881Speter svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool); 1255251881Speter 1256251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 1257251881Speter tree_conflict, eb->db, move_src_root_abspath, 1258251881Speter svn_wc_conflict_reason_moved_away, 1259251881Speter svn_wc_conflict_action_edit, 1260251881Speter move_src_root_abspath, pool, pool)); 1261251881Speter 1262251881Speter if (strcmp(db->local_abspath, move_src_root_abspath)) 1263251881Speter { 1264251881Speter /* We are raising the tree-conflict on some parent of 1265251881Speter the edit root, we won't be handling that path again 1266251881Speter so raise the conflict now. */ 1267251881Speter SVN_ERR(complete_conflict(tree_conflict, eb, 1268251881Speter move_src_root_abspath, 1269251881Speter db->old_repos_relpath, 1270251881Speter db->old_revision, db->new_relpath, 1271251881Speter svn_node_dir, svn_node_dir, 1272251881Speter pool, pool)); 1273251881Speter SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, 1274251881Speter move_src_root_abspath, 1275251881Speter tree_conflict, 1276251881Speter NULL, pool)); 1277251881Speter do_notification(eb, move_src_root_abspath, svn_node_dir, 1278251881Speter svn_wc_notify_tree_conflict, pool); 1279251881Speter } 1280251881Speter else 1281251881Speter db->edit_conflict = tree_conflict; 1282251881Speter } 1283251881Speter 1284251881Speter db->shadowed = TRUE; /* Needed for the close_directory() on the root, to 1285251881Speter make sure it doesn't use the ACTUAL tree */ 1286251881Speter } 1287251881Speter else 1288251881Speter base_status = status; 1289251881Speter 1290251881Speter if (*eb->target_basename == '\0') 1291251881Speter { 1292251881Speter /* For an update with a NULL target, this is equivalent to open_dir(): */ 1293251881Speter 1294251881Speter db->was_incomplete = (base_status == svn_wc__db_status_incomplete); 1295251881Speter 1296251881Speter /* ### TODO: Add some tree conflict and obstruction detection, etc. like 1297251881Speter open_directory() does. 1298251881Speter (or find a way to reuse that code here) 1299251881Speter 1300251881Speter ### BH 2013: I don't think we need all of the detection here, as the 1301251881Speter user explicitly asked to update this node. So we don't 1302251881Speter have to tell that it is a local replacement/delete. 1303251881Speter */ 1304251881Speter 1305251881Speter SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, 1306251881Speter db->local_abspath, 1307251881Speter db->new_relpath, 1308251881Speter *eb->target_revision, 1309251881Speter pool)); 1310251881Speter } 1311251881Speter 1312251881Speter return SVN_NO_ERROR; 1313251881Speter} 1314251881Speter 1315251881Speter 1316251881Speter/* ===================================================================== */ 1317251881Speter/* Checking for local modifications. */ 1318251881Speter 1319251881Speter/* A baton for use with modcheck_found_entry(). */ 1320251881Spetertypedef struct modcheck_baton_t { 1321251881Speter svn_wc__db_t *db; /* wc_db to access nodes */ 1322251881Speter svn_boolean_t found_mod; /* whether a modification has been found */ 1323251881Speter svn_boolean_t found_not_delete; /* Found a not-delete modification */ 1324251881Speter} modcheck_baton_t; 1325251881Speter 1326251881Speter/* An implementation of svn_wc_status_func4_t. */ 1327251881Speterstatic svn_error_t * 1328251881Spetermodcheck_callback(void *baton, 1329251881Speter const char *local_abspath, 1330251881Speter const svn_wc_status3_t *status, 1331251881Speter apr_pool_t *scratch_pool) 1332251881Speter{ 1333251881Speter modcheck_baton_t *mb = baton; 1334251881Speter 1335251881Speter switch (status->node_status) 1336251881Speter { 1337251881Speter case svn_wc_status_normal: 1338251881Speter case svn_wc_status_incomplete: 1339251881Speter case svn_wc_status_ignored: 1340251881Speter case svn_wc_status_none: 1341251881Speter case svn_wc_status_unversioned: 1342251881Speter case svn_wc_status_external: 1343251881Speter break; 1344251881Speter 1345251881Speter case svn_wc_status_deleted: 1346251881Speter mb->found_mod = TRUE; 1347251881Speter break; 1348251881Speter 1349251881Speter case svn_wc_status_missing: 1350251881Speter case svn_wc_status_obstructed: 1351251881Speter mb->found_mod = TRUE; 1352251881Speter mb->found_not_delete = TRUE; 1353251881Speter /* Exit from the status walker: We know what we want to know */ 1354251881Speter return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); 1355251881Speter 1356251881Speter default: 1357251881Speter case svn_wc_status_added: 1358251881Speter case svn_wc_status_replaced: 1359251881Speter case svn_wc_status_modified: 1360251881Speter mb->found_mod = TRUE; 1361251881Speter mb->found_not_delete = TRUE; 1362251881Speter /* Exit from the status walker: We know what we want to know */ 1363251881Speter return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); 1364251881Speter } 1365251881Speter 1366251881Speter return SVN_NO_ERROR; 1367251881Speter} 1368251881Speter 1369251881Speter 1370251881Speter/* Set *MODIFIED to true iff there are any local modifications within the 1371251881Speter * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED 1372251881Speter * is set to true and all the local modifications were deletes then set 1373251881Speter * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH 1374251881Speter * may be a file or a directory. */ 1375251881Spetersvn_error_t * 1376251881Spetersvn_wc__node_has_local_mods(svn_boolean_t *modified, 1377251881Speter svn_boolean_t *all_edits_are_deletes, 1378251881Speter svn_wc__db_t *db, 1379251881Speter const char *local_abspath, 1380251881Speter svn_cancel_func_t cancel_func, 1381251881Speter void *cancel_baton, 1382251881Speter apr_pool_t *scratch_pool) 1383251881Speter{ 1384251881Speter modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE }; 1385251881Speter svn_error_t *err; 1386251881Speter 1387251881Speter modcheck_baton.db = db; 1388251881Speter 1389251881Speter /* Walk the WC tree for status with depth infinity, looking for any local 1390251881Speter * modifications. If it's a "sparse" directory, that's OK: there can be 1391251881Speter * no local mods in the pieces that aren't present in the WC. */ 1392251881Speter 1393251881Speter err = svn_wc__internal_walk_status(db, local_abspath, 1394251881Speter svn_depth_infinity, 1395251881Speter FALSE, FALSE, FALSE, NULL, 1396251881Speter modcheck_callback, &modcheck_baton, 1397251881Speter cancel_func, cancel_baton, 1398251881Speter scratch_pool); 1399251881Speter 1400251881Speter if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) 1401251881Speter svn_error_clear(err); 1402251881Speter else 1403251881Speter SVN_ERR(err); 1404251881Speter 1405251881Speter *modified = modcheck_baton.found_mod; 1406251881Speter *all_edits_are_deletes = (modcheck_baton.found_mod 1407251881Speter && !modcheck_baton.found_not_delete); 1408251881Speter 1409251881Speter return SVN_NO_ERROR; 1410251881Speter} 1411251881Speter 1412251881Speter/* Indicates an unset svn_wc_conflict_reason_t. */ 1413251881Speter#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1) 1414251881Speter 1415251881Speter/* Check whether the incoming change ACTION on FULL_PATH would conflict with 1416251881Speter * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with 1417251881Speter * LOCAL_ABSPATH as the victim. 1418251881Speter * 1419251881Speter * The edit baton EB gives information including whether the operation is 1420251881Speter * an update or a switch. 1421251881Speter * 1422251881Speter * WORKING_STATUS is the current node status of LOCAL_ABSPATH 1423251881Speter * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists 1424251881Speter * for this node. In that case the on disk type is compared to EXPECTED_KIND. 1425251881Speter * 1426251881Speter * If a tree conflict reason was found for the incoming action, the resulting 1427251881Speter * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL, 1428251881Speter * while *PCONFLICT is always overwritten. 1429251881Speter * 1430251881Speter * The tree conflict is allocated in RESULT_POOL. Temporary allocations use 1431251881Speter * SCRATCH_POOL. 1432251881Speter */ 1433251881Speterstatic svn_error_t * 1434251881Spetercheck_tree_conflict(svn_skel_t **pconflict, 1435251881Speter struct edit_baton *eb, 1436251881Speter const char *local_abspath, 1437251881Speter svn_wc__db_status_t working_status, 1438251881Speter svn_boolean_t exists_in_repos, 1439251881Speter svn_node_kind_t expected_kind, 1440251881Speter svn_wc_conflict_action_t action, 1441251881Speter apr_pool_t *result_pool, 1442251881Speter apr_pool_t *scratch_pool) 1443251881Speter{ 1444251881Speter svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE; 1445251881Speter svn_boolean_t modified = FALSE; 1446251881Speter svn_boolean_t all_mods_are_deletes = FALSE; 1447251881Speter const char *move_src_op_root_abspath = NULL; 1448251881Speter 1449251881Speter *pconflict = NULL; 1450251881Speter 1451251881Speter /* Find out if there are any local changes to this node that may 1452251881Speter * be the "reason" of a tree-conflict with the incoming "action". */ 1453251881Speter switch (working_status) 1454251881Speter { 1455251881Speter case svn_wc__db_status_added: 1456251881Speter case svn_wc__db_status_moved_here: 1457251881Speter case svn_wc__db_status_copied: 1458251881Speter if (!exists_in_repos) 1459251881Speter { 1460251881Speter /* The node is locally added, and it did not exist before. This 1461251881Speter * is an 'update', so the local add can only conflict with an 1462251881Speter * incoming 'add'. In fact, if we receive anything else than an 1463251881Speter * svn_wc_conflict_action_add (which includes 'added', 1464251881Speter * 'copied-here' and 'moved-here') during update on a node that 1465251881Speter * did not exist before, then something is very wrong. 1466251881Speter * Note that if there was no action on the node, this code 1467251881Speter * would not have been called in the first place. */ 1468251881Speter SVN_ERR_ASSERT(action == svn_wc_conflict_action_add); 1469251881Speter 1470251881Speter /* Scan the addition in case our caller didn't. */ 1471251881Speter if (working_status == svn_wc__db_status_added) 1472251881Speter SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL, 1473251881Speter NULL, NULL, NULL, NULL, 1474251881Speter NULL, NULL, 1475251881Speter eb->db, local_abspath, 1476251881Speter scratch_pool, scratch_pool)); 1477251881Speter 1478251881Speter if (working_status == svn_wc__db_status_moved_here) 1479251881Speter reason = svn_wc_conflict_reason_moved_here; 1480251881Speter else 1481251881Speter reason = svn_wc_conflict_reason_added; 1482251881Speter } 1483251881Speter else 1484251881Speter { 1485251881Speter /* The node is locally replaced but could also be moved-away. */ 1486251881Speter SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL, 1487251881Speter &move_src_op_root_abspath, 1488251881Speter eb->db, local_abspath, 1489251881Speter scratch_pool, scratch_pool)); 1490251881Speter if (move_src_op_root_abspath) 1491251881Speter reason = svn_wc_conflict_reason_moved_away; 1492251881Speter else 1493251881Speter reason = svn_wc_conflict_reason_replaced; 1494251881Speter } 1495251881Speter break; 1496251881Speter 1497251881Speter 1498251881Speter case svn_wc__db_status_deleted: 1499251881Speter { 1500251881Speter SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL, 1501251881Speter &move_src_op_root_abspath, 1502251881Speter eb->db, local_abspath, 1503251881Speter scratch_pool, scratch_pool)); 1504251881Speter if (move_src_op_root_abspath) 1505251881Speter reason = svn_wc_conflict_reason_moved_away; 1506251881Speter else 1507251881Speter reason = svn_wc_conflict_reason_deleted; 1508251881Speter } 1509251881Speter break; 1510251881Speter 1511251881Speter case svn_wc__db_status_incomplete: 1512251881Speter /* We used svn_wc__db_read_info(), so 'incomplete' means 1513251881Speter * - there is no node in the WORKING tree 1514251881Speter * - a BASE node is known to exist 1515251881Speter * So the node exists and is essentially 'normal'. We still need to 1516251881Speter * check prop and text mods, and those checks will retrieve the 1517251881Speter * missing information (hopefully). */ 1518251881Speter case svn_wc__db_status_normal: 1519251881Speter if (action == svn_wc_conflict_action_edit) 1520251881Speter { 1521251881Speter /* An edit onto a local edit or onto *no* local changes is no 1522251881Speter * tree-conflict. (It's possibly a text- or prop-conflict, 1523251881Speter * but we don't handle those here.) 1524251881Speter * 1525251881Speter * Except when there is a local obstruction 1526251881Speter */ 1527251881Speter if (exists_in_repos) 1528251881Speter { 1529251881Speter svn_node_kind_t disk_kind; 1530251881Speter 1531251881Speter SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, 1532251881Speter scratch_pool)); 1533251881Speter 1534251881Speter if (disk_kind != expected_kind && disk_kind != svn_node_none) 1535251881Speter { 1536251881Speter reason = svn_wc_conflict_reason_obstructed; 1537251881Speter break; 1538251881Speter } 1539251881Speter 1540251881Speter } 1541251881Speter return SVN_NO_ERROR; 1542251881Speter } 1543251881Speter 1544251881Speter /* Replace is handled as delete and then specifically in 1545251881Speter add_directory() and add_file(), so we only expect deletes here */ 1546251881Speter SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete); 1547251881Speter 1548251881Speter /* Check if the update wants to delete or replace a locally 1549251881Speter * modified node. */ 1550251881Speter 1551251881Speter 1552251881Speter /* Do a deep tree detection of local changes. The update editor will 1553251881Speter * not visit the subdirectories of a directory that it wants to delete. 1554251881Speter * Therefore, we need to start a separate crawl here. */ 1555251881Speter 1556251881Speter SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes, 1557251881Speter eb->db, local_abspath, 1558251881Speter eb->cancel_func, eb->cancel_baton, 1559251881Speter scratch_pool)); 1560251881Speter 1561251881Speter if (modified) 1562251881Speter { 1563251881Speter if (all_mods_are_deletes) 1564251881Speter reason = svn_wc_conflict_reason_deleted; 1565251881Speter else 1566251881Speter reason = svn_wc_conflict_reason_edited; 1567251881Speter } 1568251881Speter break; 1569251881Speter 1570251881Speter case svn_wc__db_status_server_excluded: 1571251881Speter /* Not allowed to view the node. Not allowed to report tree 1572251881Speter * conflicts. */ 1573251881Speter case svn_wc__db_status_excluded: 1574251881Speter /* Locally marked as excluded. No conflicts wanted. */ 1575251881Speter case svn_wc__db_status_not_present: 1576251881Speter /* A committed delete (but parent not updated). The delete is 1577251881Speter committed, so no conflict possible during update. */ 1578251881Speter return SVN_NO_ERROR; 1579251881Speter 1580251881Speter case svn_wc__db_status_base_deleted: 1581251881Speter /* An internal status. Should never show up here. */ 1582251881Speter SVN_ERR_MALFUNCTION(); 1583251881Speter break; 1584251881Speter 1585251881Speter } 1586251881Speter 1587251881Speter if (reason == SVN_WC_CONFLICT_REASON_NONE) 1588251881Speter /* No conflict with the current action. */ 1589251881Speter return SVN_NO_ERROR; 1590251881Speter 1591251881Speter 1592251881Speter /* Sanity checks. Note that if there was no action on the node, this function 1593251881Speter * would not have been called in the first place.*/ 1594251881Speter if (reason == svn_wc_conflict_reason_edited 1595251881Speter || reason == svn_wc_conflict_reason_obstructed 1596251881Speter || reason == svn_wc_conflict_reason_deleted 1597251881Speter || reason == svn_wc_conflict_reason_moved_away 1598251881Speter || reason == svn_wc_conflict_reason_replaced) 1599251881Speter { 1600251881Speter /* When the node existed before (it was locally deleted, replaced or 1601251881Speter * edited), then 'update' cannot add it "again". So it can only send 1602251881Speter * _action_edit, _delete or _replace. */ 1603251881Speter if (action != svn_wc_conflict_action_edit 1604251881Speter && action != svn_wc_conflict_action_delete 1605251881Speter && action != svn_wc_conflict_action_replace) 1606251881Speter return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 1607251881Speter _("Unexpected attempt to add a node at path '%s'"), 1608251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 1609251881Speter } 1610251881Speter else if (reason == svn_wc_conflict_reason_added || 1611251881Speter reason == svn_wc_conflict_reason_moved_here) 1612251881Speter { 1613251881Speter /* When the node did not exist before (it was locally added), 1614251881Speter * then 'update' cannot want to modify it in any way. 1615251881Speter * It can only send _action_add. */ 1616251881Speter if (action != svn_wc_conflict_action_add) 1617251881Speter return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 1618251881Speter _("Unexpected attempt to edit, delete, or replace " 1619251881Speter "a node at path '%s'"), 1620251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 1621251881Speter 1622251881Speter } 1623251881Speter 1624251881Speter 1625251881Speter /* A conflict was detected. Create a conflict skel to record it. */ 1626251881Speter *pconflict = svn_wc__conflict_skel_create(result_pool); 1627251881Speter 1628251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict, 1629251881Speter eb->db, local_abspath, 1630251881Speter reason, 1631251881Speter action, 1632251881Speter move_src_op_root_abspath, 1633251881Speter result_pool, scratch_pool)); 1634251881Speter 1635251881Speter return SVN_NO_ERROR; 1636251881Speter} 1637251881Speter 1638251881Speter 1639251881Speter/* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is 1640251881Speter * not a moved-away-edit conflict, set *CONFLICTED to TRUE. Otherwise 1641251881Speter * set *CONFLICTED to FALSE. 1642251881Speter */ 1643251881Speterstatic svn_error_t * 1644251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted, 1645251881Speter svn_boolean_t *ignored, 1646251881Speter svn_wc__db_t *db, 1647251881Speter const char *local_abspath, 1648251881Speter apr_pool_t *scratch_pool) 1649251881Speter{ 1650251881Speter const char *ancestor_abspath = local_abspath; 1651251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1652251881Speter 1653251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 1654251881Speter 1655251881Speter *conflicted = *ignored = FALSE; 1656251881Speter 1657251881Speter while (TRUE) 1658251881Speter { 1659251881Speter svn_boolean_t is_wc_root; 1660251881Speter 1661251881Speter svn_pool_clear(iterpool); 1662251881Speter 1663251881Speter SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db, 1664251881Speter ancestor_abspath, TRUE, 1665251881Speter scratch_pool)); 1666251881Speter if (*conflicted || *ignored) 1667251881Speter break; 1668251881Speter 1669251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath, 1670251881Speter iterpool)); 1671251881Speter if (is_wc_root) 1672251881Speter break; 1673251881Speter 1674251881Speter ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool); 1675251881Speter } 1676251881Speter 1677251881Speter svn_pool_destroy(iterpool); 1678251881Speter 1679251881Speter return SVN_NO_ERROR; 1680251881Speter} 1681251881Speter 1682251881Speter/* Temporary helper until the new conflict handling is in place */ 1683251881Speterstatic svn_error_t * 1684251881Speternode_already_conflicted(svn_boolean_t *conflicted, 1685251881Speter svn_boolean_t *conflict_ignored, 1686251881Speter svn_wc__db_t *db, 1687251881Speter const char *local_abspath, 1688251881Speter apr_pool_t *scratch_pool) 1689251881Speter{ 1690251881Speter SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db, 1691251881Speter local_abspath, FALSE, 1692251881Speter scratch_pool)); 1693251881Speter 1694251881Speter return SVN_NO_ERROR; 1695251881Speter} 1696251881Speter 1697251881Speter 1698251881Speter/* An svn_delta_editor_t function. */ 1699251881Speterstatic svn_error_t * 1700251881Speterdelete_entry(const char *path, 1701251881Speter svn_revnum_t revision, 1702251881Speter void *parent_baton, 1703251881Speter apr_pool_t *pool) 1704251881Speter{ 1705251881Speter struct dir_baton *pb = parent_baton; 1706251881Speter struct edit_baton *eb = pb->edit_baton; 1707251881Speter const char *base = svn_relpath_basename(path, NULL); 1708251881Speter const char *local_abspath; 1709251881Speter const char *repos_relpath; 1710251881Speter svn_node_kind_t kind, base_kind; 1711251881Speter svn_revnum_t old_revision; 1712251881Speter svn_boolean_t conflicted; 1713251881Speter svn_boolean_t have_work; 1714251881Speter svn_skel_t *tree_conflict = NULL; 1715251881Speter svn_wc__db_status_t status; 1716251881Speter svn_wc__db_status_t base_status; 1717251881Speter apr_pool_t *scratch_pool; 1718251881Speter svn_boolean_t deleting_target; 1719251881Speter svn_boolean_t deleting_switched; 1720251881Speter svn_boolean_t keep_as_working = FALSE; 1721251881Speter svn_boolean_t queue_deletes = TRUE; 1722251881Speter 1723251881Speter if (pb->skip_this) 1724251881Speter return SVN_NO_ERROR; 1725251881Speter 1726251881Speter scratch_pool = svn_pool_create(pb->pool); 1727251881Speter 1728251881Speter SVN_ERR(mark_directory_edited(pb, scratch_pool)); 1729251881Speter 1730251881Speter SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base, 1731251881Speter scratch_pool)); 1732251881Speter 1733251881Speter deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0); 1734251881Speter 1735251881Speter /* Detect obstructing working copies */ 1736251881Speter { 1737251881Speter svn_boolean_t is_root; 1738251881Speter 1739251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath, 1740251881Speter scratch_pool)); 1741251881Speter 1742251881Speter if (is_root) 1743251881Speter { 1744251881Speter /* Just skip this node; a future update will handle it */ 1745251881Speter SVN_ERR(remember_skipped_tree(eb, local_abspath, pool)); 1746251881Speter do_notification(eb, local_abspath, svn_node_unknown, 1747251881Speter svn_wc_notify_update_skip_obstruction, scratch_pool); 1748251881Speter 1749251881Speter svn_pool_destroy(scratch_pool); 1750251881Speter 1751251881Speter return SVN_NO_ERROR; 1752251881Speter } 1753251881Speter } 1754251881Speter 1755251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath, 1756251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1757251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1758251881Speter &conflicted, NULL, NULL, NULL, 1759251881Speter NULL, NULL, &have_work, 1760251881Speter eb->db, local_abspath, 1761251881Speter scratch_pool, scratch_pool)); 1762251881Speter 1763251881Speter if (!have_work) 1764251881Speter { 1765251881Speter base_status = status; 1766251881Speter base_kind = kind; 1767251881Speter } 1768251881Speter else 1769251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision, 1770251881Speter &repos_relpath, 1771251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1772251881Speter NULL, NULL, NULL, NULL, NULL, 1773251881Speter eb->db, local_abspath, 1774251881Speter scratch_pool, scratch_pool)); 1775251881Speter 1776251881Speter if (pb->old_repos_relpath && repos_relpath) 1777251881Speter { 1778251881Speter const char *expected_name; 1779251881Speter 1780251881Speter expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath, 1781251881Speter repos_relpath); 1782251881Speter 1783251881Speter deleting_switched = (!expected_name || strcmp(expected_name, base) != 0); 1784251881Speter } 1785251881Speter else 1786251881Speter deleting_switched = FALSE; 1787251881Speter 1788251881Speter /* Is this path a conflict victim? */ 1789251881Speter if (pb->shadowed) 1790251881Speter conflicted = FALSE; /* Conflict applies to WORKING */ 1791251881Speter else if (conflicted) 1792251881Speter SVN_ERR(node_already_conflicted(&conflicted, NULL, 1793251881Speter eb->db, local_abspath, scratch_pool)); 1794251881Speter if (conflicted) 1795251881Speter { 1796251881Speter SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool)); 1797251881Speter 1798251881Speter do_notification(eb, local_abspath, svn_node_unknown, 1799251881Speter svn_wc_notify_skip_conflicted, 1800251881Speter scratch_pool); 1801251881Speter 1802251881Speter svn_pool_destroy(scratch_pool); 1803251881Speter 1804251881Speter return SVN_NO_ERROR; 1805251881Speter } 1806251881Speter 1807251881Speter 1808251881Speter /* Receive the remote removal of excluded/server-excluded/not present node. 1809251881Speter Do not notify, but perform the change even when the node is shadowed */ 1810251881Speter if (base_status == svn_wc__db_status_not_present 1811251881Speter || base_status == svn_wc__db_status_excluded 1812251881Speter || base_status == svn_wc__db_status_server_excluded) 1813251881Speter { 1814251881Speter SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, 1815251881Speter FALSE /* keep_as_working */, 1816251881Speter FALSE /* queue_deletes */, 1817253734Speter FALSE /* remove_locks */, 1818251881Speter SVN_INVALID_REVNUM /* not_present_rev */, 1819251881Speter NULL, NULL, 1820251881Speter scratch_pool)); 1821251881Speter 1822251881Speter if (deleting_target) 1823251881Speter eb->target_deleted = TRUE; 1824251881Speter 1825251881Speter svn_pool_destroy(scratch_pool); 1826251881Speter 1827251881Speter return SVN_NO_ERROR; 1828251881Speter } 1829251881Speter 1830251881Speter /* Is this path the victim of a newly-discovered tree conflict? If so, 1831251881Speter * remember it and notify the client. Then (if it was existing and 1832251881Speter * modified), re-schedule the node to be added back again, as a (modified) 1833251881Speter * copy of the previous base version. */ 1834251881Speter 1835251881Speter /* Check for conflicts only when we haven't already recorded 1836251881Speter * a tree-conflict on a parent node. */ 1837251881Speter if (!pb->shadowed && !pb->edit_obstructed) 1838251881Speter { 1839251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, 1840251881Speter status, TRUE, 1841251881Speter (kind == svn_node_dir) 1842251881Speter ? svn_node_dir 1843251881Speter : svn_node_file, 1844251881Speter svn_wc_conflict_action_delete, 1845251881Speter pb->pool, scratch_pool)); 1846251881Speter } 1847251881Speter else 1848251881Speter queue_deletes = FALSE; /* There is no in-wc representation */ 1849251881Speter 1850251881Speter if (tree_conflict != NULL) 1851251881Speter { 1852251881Speter svn_wc_conflict_reason_t reason; 1853251881Speter /* When we raise a tree conflict on a node, we don't want to mark the 1854251881Speter * node as skipped, to allow a replacement to continue doing at least 1855251881Speter * a bit of its work (possibly adding a not present node, for the 1856251881Speter * next update) */ 1857251881Speter if (!pb->deletion_conflicts) 1858251881Speter pb->deletion_conflicts = apr_hash_make(pb->pool); 1859251881Speter 1860251881Speter svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base), 1861251881Speter tree_conflict); 1862251881Speter 1863251881Speter SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 1864251881Speter eb->db, local_abspath, 1865251881Speter tree_conflict, 1866251881Speter scratch_pool, scratch_pool)); 1867251881Speter 1868251881Speter if (reason == svn_wc_conflict_reason_edited 1869251881Speter || reason == svn_wc_conflict_reason_obstructed) 1870251881Speter { 1871251881Speter /* The item exists locally and has some sort of local mod. 1872251881Speter * It no longer exists in the repository at its target URL@REV. 1873251881Speter * 1874251881Speter * To prepare the "accept mine" resolution for the tree conflict, 1875251881Speter * we must schedule the existing content for re-addition as a copy 1876251881Speter * of what it was, but with its local modifications preserved. */ 1877251881Speter keep_as_working = TRUE; 1878251881Speter 1879251881Speter /* Fall through to remove the BASE_NODEs properly, with potentially 1880251881Speter keeping a not-present marker */ 1881251881Speter } 1882251881Speter else if (reason == svn_wc_conflict_reason_deleted 1883251881Speter || reason == svn_wc_conflict_reason_moved_away 1884251881Speter || reason == svn_wc_conflict_reason_replaced) 1885251881Speter { 1886251881Speter /* The item does not exist locally because it was already shadowed. 1887251881Speter * We must complete the deletion, leaving the tree conflict info 1888251881Speter * as the only difference from a normal deletion. */ 1889251881Speter 1890251881Speter /* Fall through to the normal "delete" code path. */ 1891251881Speter } 1892251881Speter else 1893251881Speter SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */ 1894251881Speter } 1895251881Speter 1896251881Speter SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath, 1897251881Speter old_revision, NULL, 1898251881Speter (kind == svn_node_dir) 1899251881Speter ? svn_node_dir 1900251881Speter : svn_node_file, 1901251881Speter svn_node_none, 1902251881Speter pb->pool, scratch_pool)); 1903251881Speter 1904251881Speter /* Issue a wq operation to delete the BASE_NODE data and to delete actual 1905251881Speter nodes based on that from disk, but leave any WORKING_NODEs on disk. 1906251881Speter 1907251881Speter Local modifications are already turned into copies at this point. 1908251881Speter 1909251881Speter If the thing being deleted is the *target* of this update, then 1910251881Speter we need to recreate a 'deleted' entry, so that the parent can give 1911251881Speter accurate reports about itself in the future. */ 1912251881Speter if (! deleting_target && ! deleting_switched) 1913251881Speter { 1914251881Speter /* Delete, and do not leave a not-present node. */ 1915251881Speter SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, 1916253734Speter keep_as_working, queue_deletes, FALSE, 1917251881Speter SVN_INVALID_REVNUM /* not_present_rev */, 1918251881Speter tree_conflict, NULL, 1919251881Speter scratch_pool)); 1920251881Speter } 1921251881Speter else 1922251881Speter { 1923251881Speter /* Delete, leaving a not-present node. */ 1924251881Speter SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, 1925253734Speter keep_as_working, queue_deletes, FALSE, 1926251881Speter *eb->target_revision, 1927251881Speter tree_conflict, NULL, 1928251881Speter scratch_pool)); 1929251881Speter if (deleting_target) 1930251881Speter eb->target_deleted = TRUE; 1931251881Speter else 1932251881Speter { 1933251881Speter /* Don't remove the not-present marker at the final bump */ 1934251881Speter SVN_ERR(remember_skipped_tree(eb, local_abspath, pool)); 1935251881Speter } 1936251881Speter } 1937251881Speter 1938251881Speter SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath, 1939251881Speter eb->cancel_func, eb->cancel_baton, 1940251881Speter scratch_pool)); 1941251881Speter 1942251881Speter /* Notify. */ 1943251881Speter if (tree_conflict) 1944253734Speter { 1945253734Speter if (eb->conflict_func) 1946253734Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, 1947253734Speter tree_conflict, 1948253734Speter NULL /* merge_options */, 1949253734Speter eb->conflict_func, 1950253734Speter eb->conflict_baton, 1951253734Speter eb->cancel_func, 1952253734Speter eb->cancel_baton, 1953253734Speter scratch_pool)); 1954253734Speter do_notification(eb, local_abspath, svn_node_unknown, 1955253734Speter svn_wc_notify_tree_conflict, scratch_pool); 1956253734Speter } 1957251881Speter else 1958251881Speter { 1959251881Speter svn_wc_notify_action_t action = svn_wc_notify_update_delete; 1960251881Speter svn_node_kind_t node_kind; 1961251881Speter 1962251881Speter if (pb->shadowed || pb->edit_obstructed) 1963251881Speter action = svn_wc_notify_update_shadowed_delete; 1964251881Speter 1965251881Speter if (kind == svn_node_dir) 1966251881Speter node_kind = svn_node_dir; 1967251881Speter else 1968251881Speter node_kind = svn_node_file; 1969251881Speter 1970251881Speter do_notification(eb, local_abspath, node_kind, action, scratch_pool); 1971251881Speter } 1972251881Speter 1973251881Speter svn_pool_destroy(scratch_pool); 1974251881Speter 1975251881Speter return SVN_NO_ERROR; 1976251881Speter} 1977251881Speter 1978251881Speter/* An svn_delta_editor_t function. */ 1979251881Speterstatic svn_error_t * 1980251881Speteradd_directory(const char *path, 1981251881Speter void *parent_baton, 1982251881Speter const char *copyfrom_path, 1983251881Speter svn_revnum_t copyfrom_rev, 1984251881Speter apr_pool_t *pool, 1985251881Speter void **child_baton) 1986251881Speter{ 1987251881Speter struct dir_baton *pb = parent_baton; 1988251881Speter struct edit_baton *eb = pb->edit_baton; 1989251881Speter struct dir_baton *db; 1990251881Speter svn_node_kind_t kind; 1991251881Speter svn_wc__db_status_t status; 1992251881Speter svn_node_kind_t wc_kind; 1993251881Speter svn_boolean_t conflicted; 1994251881Speter svn_boolean_t conflict_ignored = FALSE; 1995251881Speter svn_boolean_t versioned_locally_and_present; 1996251881Speter svn_skel_t *tree_conflict = NULL; 1997251881Speter svn_error_t *err; 1998251881Speter 1999251881Speter SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev))); 2000251881Speter 2001251881Speter SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool)); 2002251881Speter *child_baton = db; 2003251881Speter 2004251881Speter if (db->skip_this) 2005251881Speter return SVN_NO_ERROR; 2006251881Speter 2007251881Speter SVN_ERR(mark_directory_edited(db, pool)); 2008251881Speter 2009251881Speter if (strcmp(eb->target_abspath, db->local_abspath) == 0) 2010251881Speter { 2011251881Speter /* The target of the edit is being added, give it the requested 2012251881Speter depth of the edit (but convert svn_depth_unknown to 2013251881Speter svn_depth_infinity). */ 2014251881Speter db->ambient_depth = (eb->requested_depth == svn_depth_unknown) 2015251881Speter ? svn_depth_infinity : eb->requested_depth; 2016251881Speter } 2017251881Speter else if (eb->requested_depth == svn_depth_immediates 2018251881Speter || (eb->requested_depth == svn_depth_unknown 2019251881Speter && pb->ambient_depth == svn_depth_immediates)) 2020251881Speter { 2021251881Speter db->ambient_depth = svn_depth_empty; 2022251881Speter } 2023251881Speter else 2024251881Speter { 2025251881Speter db->ambient_depth = svn_depth_infinity; 2026251881Speter } 2027251881Speter 2028251881Speter /* It may not be named the same as the administrative directory. */ 2029251881Speter if (svn_wc_is_adm_dir(db->name, pool)) 2030251881Speter return svn_error_createf( 2031251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 2032251881Speter _("Failed to add directory '%s': object of the same name as the " 2033251881Speter "administrative directory"), 2034251881Speter svn_dirent_local_style(db->local_abspath, pool)); 2035251881Speter 2036251881Speter SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool)); 2037251881Speter 2038251881Speter err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, 2039251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2040251881Speter NULL, NULL, NULL, NULL, NULL, 2041251881Speter &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, 2042251881Speter eb->db, db->local_abspath, db->pool, db->pool); 2043251881Speter if (err) 2044251881Speter { 2045251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2046251881Speter return svn_error_trace(err); 2047251881Speter 2048251881Speter svn_error_clear(err); 2049251881Speter wc_kind = svn_node_unknown; 2050251881Speter status = svn_wc__db_status_normal; 2051251881Speter conflicted = FALSE; 2052251881Speter 2053251881Speter versioned_locally_and_present = FALSE; 2054251881Speter } 2055251881Speter else if (wc_kind == svn_node_dir 2056251881Speter && status == svn_wc__db_status_normal) 2057251881Speter { 2058251881Speter /* !! We found the root of a separate working copy obstructing the wc !! 2059251881Speter 2060251881Speter If the directory would be part of our own working copy then 2061251881Speter we wouldn't have been called as an add_directory(). 2062251881Speter 2063251881Speter The only thing we can do is add a not-present node, to allow 2064251881Speter a future update to bring in the new files when the problem is 2065251881Speter resolved. Note that svn_wc__db_base_add_not_present_node() 2066251881Speter explicitly adds the node into the parent's node database. */ 2067251881Speter 2068251881Speter SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath, 2069251881Speter db->new_relpath, 2070251881Speter eb->repos_root, 2071251881Speter eb->repos_uuid, 2072251881Speter *eb->target_revision, 2073251881Speter svn_node_file, 2074251881Speter NULL, NULL, 2075251881Speter pool)); 2076251881Speter 2077251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2078251881Speter db->skip_this = TRUE; 2079251881Speter db->already_notified = TRUE; 2080251881Speter 2081251881Speter do_notification(eb, db->local_abspath, svn_node_dir, 2082251881Speter svn_wc_notify_update_skip_obstruction, pool); 2083251881Speter 2084251881Speter return SVN_NO_ERROR; 2085251881Speter } 2086251881Speter else if (status == svn_wc__db_status_normal 2087251881Speter && (wc_kind == svn_node_file 2088251881Speter || wc_kind == svn_node_symlink)) 2089251881Speter { 2090251881Speter /* We found a file external occupating the place we need in BASE. 2091251881Speter 2092251881Speter We can't add a not-present node in this case as that would overwrite 2093251881Speter the file external. Luckily the file external itself stops us from 2094251881Speter forgetting a child of this parent directory like an obstructing 2095251881Speter working copy would. 2096251881Speter 2097251881Speter The reason we get here is that the adm crawler doesn't report 2098251881Speter file externals. 2099251881Speter */ 2100251881Speter 2101251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2102251881Speter db->skip_this = TRUE; 2103251881Speter db->already_notified = TRUE; 2104251881Speter 2105251881Speter do_notification(eb, db->local_abspath, svn_node_file, 2106251881Speter svn_wc_notify_update_skip_obstruction, pool); 2107251881Speter 2108251881Speter return SVN_NO_ERROR; 2109251881Speter } 2110251881Speter else if (wc_kind == svn_node_unknown) 2111251881Speter versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ 2112251881Speter else 2113251881Speter versioned_locally_and_present = IS_NODE_PRESENT(status); 2114251881Speter 2115251881Speter /* Is this path a conflict victim? */ 2116251881Speter if (conflicted) 2117251881Speter { 2118251881Speter if (pb->deletion_conflicts) 2119251881Speter tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name); 2120251881Speter 2121251881Speter if (tree_conflict) 2122251881Speter { 2123251881Speter svn_wc_conflict_reason_t reason; 2124251881Speter /* So this deletion wasn't just a deletion, it is actually a 2125251881Speter replacement. Let's install a better tree conflict. */ 2126251881Speter 2127251881Speter /* ### Should store the conflict in DB to allow reinstalling 2128251881Speter ### with theoretically more data in close_directory() */ 2129251881Speter 2130251881Speter SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 2131251881Speter eb->db, 2132251881Speter db->local_abspath, 2133251881Speter tree_conflict, 2134251881Speter db->pool, db->pool)); 2135251881Speter 2136251881Speter tree_conflict = svn_wc__conflict_skel_create(db->pool); 2137251881Speter 2138251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 2139251881Speter tree_conflict, 2140251881Speter eb->db, db->local_abspath, 2141251881Speter reason, svn_wc_conflict_action_replace, 2142251881Speter NULL, 2143251881Speter db->pool, db->pool)); 2144251881Speter 2145251881Speter /* And now stop checking for conflicts here and just perform 2146251881Speter a shadowed update */ 2147251881Speter db->edit_conflict = tree_conflict; /* Cache for close_directory */ 2148251881Speter tree_conflict = NULL; /* No direct notification */ 2149251881Speter db->shadowed = TRUE; /* Just continue */ 2150251881Speter conflicted = FALSE; /* No skip */ 2151251881Speter } 2152251881Speter else 2153251881Speter SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 2154251881Speter eb->db, db->local_abspath, pool)); 2155251881Speter } 2156251881Speter 2157251881Speter /* Now the "usual" behaviour if already conflicted. Skip it. */ 2158251881Speter if (conflicted) 2159251881Speter { 2160251881Speter /* Record this conflict so that its descendants are skipped silently. */ 2161251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2162251881Speter 2163251881Speter db->skip_this = TRUE; 2164251881Speter db->already_notified = TRUE; 2165251881Speter 2166251881Speter /* We skip this node, but once the update completes the parent node will 2167251881Speter be updated to the new revision. So a future recursive update of the 2168251881Speter parent will not bring in this new node as the revision of the parent 2169251881Speter describes to the repository that all children are available. 2170251881Speter 2171251881Speter To resolve this problem, we add a not-present node to allow bringing 2172251881Speter the node in once this conflict is resolved. 2173251881Speter 2174251881Speter Note that we can safely assume that no present base node exists, 2175251881Speter because then we would not have received an add_directory. 2176251881Speter */ 2177251881Speter SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath, 2178251881Speter db->new_relpath, 2179251881Speter eb->repos_root, 2180251881Speter eb->repos_uuid, 2181251881Speter *eb->target_revision, 2182251881Speter svn_node_dir, 2183251881Speter NULL, NULL, 2184251881Speter pool)); 2185251881Speter 2186251881Speter /* ### TODO: Also print victim_path in the skip msg. */ 2187251881Speter do_notification(eb, db->local_abspath, svn_node_dir, 2188251881Speter svn_wc_notify_skip_conflicted, pool); 2189251881Speter return SVN_NO_ERROR; 2190251881Speter } 2191251881Speter else if (conflict_ignored) 2192251881Speter { 2193251881Speter db->shadowed = TRUE; 2194251881Speter } 2195251881Speter 2196251881Speter if (db->shadowed) 2197251881Speter { 2198251881Speter /* Nothing to check; does not and will not exist in working copy */ 2199251881Speter } 2200251881Speter else if (versioned_locally_and_present) 2201251881Speter { 2202251881Speter /* What to do with a versioned or schedule-add dir: 2203251881Speter 2204251881Speter A dir already added without history is OK. Set add_existed 2205251881Speter so that user notification is delayed until after any prop 2206251881Speter conflicts have been found. 2207251881Speter 2208251881Speter An existing versioned dir is an error. In the future we may 2209251881Speter relax this restriction and simply update such dirs. 2210251881Speter 2211251881Speter A dir added with history is a tree conflict. */ 2212251881Speter 2213251881Speter svn_boolean_t local_is_non_dir; 2214251881Speter svn_wc__db_status_t add_status = svn_wc__db_status_normal; 2215251881Speter 2216251881Speter /* Is the local add a copy? */ 2217251881Speter if (status == svn_wc__db_status_added) 2218251881Speter SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL, 2219251881Speter NULL, NULL, NULL, NULL, 2220251881Speter eb->db, db->local_abspath, 2221251881Speter pool, pool)); 2222251881Speter 2223251881Speter 2224251881Speter /* Is there *something* that is not a dir? */ 2225251881Speter local_is_non_dir = (wc_kind != svn_node_dir 2226251881Speter && status != svn_wc__db_status_deleted); 2227251881Speter 2228251881Speter /* Do tree conflict checking if 2229251881Speter * - if there is a local copy. 2230251881Speter * - if this is a switch operation 2231251881Speter * - the node kinds mismatch 2232251881Speter * 2233251881Speter * During switch, local adds at the same path as incoming adds get 2234251881Speter * "lost" in that switching back to the original will no longer have the 2235251881Speter * local add. So switch always alerts the user with a tree conflict. */ 2236251881Speter if (!eb->adds_as_modification 2237251881Speter || local_is_non_dir 2238251881Speter || add_status != svn_wc__db_status_added) 2239251881Speter { 2240251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, 2241251881Speter db->local_abspath, 2242251881Speter status, FALSE, svn_node_none, 2243251881Speter svn_wc_conflict_action_add, 2244251881Speter pool, pool)); 2245251881Speter } 2246251881Speter 2247251881Speter if (tree_conflict == NULL) 2248251881Speter db->add_existed = TRUE; /* Take over WORKING */ 2249251881Speter else 2250251881Speter db->shadowed = TRUE; /* Only update BASE */ 2251251881Speter } 2252251881Speter else if (kind != svn_node_none) 2253251881Speter { 2254251881Speter /* There's an unversioned node at this path. */ 2255251881Speter db->obstruction_found = TRUE; 2256251881Speter 2257251881Speter /* Unversioned, obstructing dirs are handled by prop merge/conflict, 2258251881Speter * if unversioned obstructions are allowed. */ 2259251881Speter if (! (kind == svn_node_dir && eb->allow_unver_obstructions)) 2260251881Speter { 2261251881Speter /* Bring in the node as deleted */ /* ### Obstructed Conflict */ 2262251881Speter db->shadowed = TRUE; 2263251881Speter 2264251881Speter /* Mark a conflict */ 2265251881Speter tree_conflict = svn_wc__conflict_skel_create(db->pool); 2266251881Speter 2267251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 2268251881Speter tree_conflict, 2269251881Speter eb->db, db->local_abspath, 2270251881Speter svn_wc_conflict_reason_unversioned, 2271251881Speter svn_wc_conflict_action_add, NULL, 2272251881Speter db->pool, pool)); 2273251881Speter db->edit_conflict = tree_conflict; 2274251881Speter } 2275251881Speter } 2276251881Speter 2277251881Speter if (tree_conflict) 2278251881Speter SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath, 2279251881Speter db->old_repos_relpath, db->old_revision, 2280251881Speter db->new_relpath, 2281251881Speter wc_kind, 2282251881Speter svn_node_dir, 2283251881Speter db->pool, pool)); 2284251881Speter 2285251881Speter SVN_ERR(svn_wc__db_base_add_incomplete_directory( 2286251881Speter eb->db, db->local_abspath, 2287251881Speter db->new_relpath, 2288251881Speter eb->repos_root, 2289251881Speter eb->repos_uuid, 2290251881Speter *eb->target_revision, 2291251881Speter db->ambient_depth, 2292251881Speter (db->shadowed && db->obstruction_found), 2293251881Speter (! db->shadowed 2294251881Speter && status == svn_wc__db_status_added), 2295251881Speter tree_conflict, NULL, 2296251881Speter pool)); 2297251881Speter 2298251881Speter /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just 2299251881Speter updating the DB */ 2300251881Speter if (!db->shadowed) 2301251881Speter SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool)); 2302251881Speter 2303251881Speter if (tree_conflict != NULL) 2304251881Speter { 2305253734Speter if (eb->conflict_func) 2306253734Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, 2307253734Speter tree_conflict, 2308253734Speter NULL /* merge_options */, 2309253734Speter eb->conflict_func, 2310253734Speter eb->conflict_baton, 2311253734Speter eb->cancel_func, 2312253734Speter eb->cancel_baton, 2313253734Speter pool)); 2314253734Speter 2315251881Speter db->already_notified = TRUE; 2316251881Speter do_notification(eb, db->local_abspath, svn_node_dir, 2317251881Speter svn_wc_notify_tree_conflict, pool); 2318251881Speter } 2319251881Speter 2320251881Speter 2321251881Speter /* If this add was obstructed by dir scheduled for addition without 2322251881Speter history let close_directory() handle the notification because there 2323251881Speter might be properties to deal with. If PATH was added inside a locally 2324251881Speter deleted tree, then suppress notification, a tree conflict was already 2325251881Speter issued. */ 2326251881Speter if (eb->notify_func && !db->already_notified && !db->add_existed) 2327251881Speter { 2328251881Speter svn_wc_notify_action_t action; 2329251881Speter 2330251881Speter if (db->shadowed) 2331251881Speter action = svn_wc_notify_update_shadowed_add; 2332251881Speter else if (db->obstruction_found || db->add_existed) 2333251881Speter action = svn_wc_notify_exists; 2334251881Speter else 2335251881Speter action = svn_wc_notify_update_add; 2336251881Speter 2337251881Speter db->already_notified = TRUE; 2338251881Speter 2339251881Speter do_notification(eb, db->local_abspath, svn_node_dir, action, pool); 2340251881Speter } 2341251881Speter 2342251881Speter return SVN_NO_ERROR; 2343251881Speter} 2344251881Speter 2345251881Speter/* An svn_delta_editor_t function. */ 2346251881Speterstatic svn_error_t * 2347251881Speteropen_directory(const char *path, 2348251881Speter void *parent_baton, 2349251881Speter svn_revnum_t base_revision, 2350251881Speter apr_pool_t *pool, 2351251881Speter void **child_baton) 2352251881Speter{ 2353251881Speter struct dir_baton *db, *pb = parent_baton; 2354251881Speter struct edit_baton *eb = pb->edit_baton; 2355251881Speter svn_boolean_t have_work; 2356251881Speter svn_boolean_t conflicted; 2357251881Speter svn_boolean_t conflict_ignored = FALSE; 2358251881Speter svn_skel_t *tree_conflict = NULL; 2359251881Speter svn_wc__db_status_t status, base_status; 2360251881Speter svn_node_kind_t wc_kind; 2361251881Speter 2362251881Speter SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool)); 2363251881Speter *child_baton = db; 2364251881Speter 2365251881Speter if (db->skip_this) 2366251881Speter return SVN_NO_ERROR; 2367251881Speter 2368251881Speter /* Detect obstructing working copies */ 2369251881Speter { 2370251881Speter svn_boolean_t is_root; 2371251881Speter 2372251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath, 2373251881Speter pool)); 2374251881Speter 2375251881Speter if (is_root) 2376251881Speter { 2377251881Speter /* Just skip this node; a future update will handle it */ 2378251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2379251881Speter db->skip_this = TRUE; 2380251881Speter db->already_notified = TRUE; 2381251881Speter 2382251881Speter do_notification(eb, db->local_abspath, svn_node_dir, 2383251881Speter svn_wc_notify_update_skip_obstruction, pool); 2384251881Speter 2385251881Speter return SVN_NO_ERROR; 2386251881Speter } 2387251881Speter } 2388251881Speter 2389251881Speter /* We should have a write lock on every directory touched. */ 2390251881Speter SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool)); 2391251881Speter 2392251881Speter SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision, 2393251881Speter &db->old_repos_relpath, NULL, NULL, 2394251881Speter &db->changed_rev, &db->changed_date, 2395251881Speter &db->changed_author, &db->ambient_depth, 2396251881Speter NULL, NULL, NULL, NULL, 2397251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2398251881Speter &conflicted, NULL, NULL, NULL, 2399251881Speter NULL, NULL, &have_work, 2400251881Speter eb->db, db->local_abspath, 2401251881Speter db->pool, pool)); 2402251881Speter 2403251881Speter if (!have_work) 2404251881Speter base_status = status; 2405251881Speter else 2406251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision, 2407251881Speter &db->old_repos_relpath, NULL, NULL, 2408251881Speter &db->changed_rev, &db->changed_date, 2409251881Speter &db->changed_author, &db->ambient_depth, 2410251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2411251881Speter eb->db, db->local_abspath, 2412251881Speter db->pool, pool)); 2413251881Speter 2414251881Speter db->was_incomplete = (base_status == svn_wc__db_status_incomplete); 2415251881Speter 2416251881Speter /* Is this path a conflict victim? */ 2417251881Speter if (db->shadowed) 2418251881Speter conflicted = FALSE; /* Conflict applies to WORKING */ 2419251881Speter else if (conflicted) 2420251881Speter SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 2421251881Speter eb->db, db->local_abspath, pool)); 2422251881Speter if (conflicted) 2423251881Speter { 2424251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2425251881Speter 2426251881Speter db->skip_this = TRUE; 2427251881Speter db->already_notified = TRUE; 2428251881Speter 2429251881Speter do_notification(eb, db->local_abspath, svn_node_unknown, 2430251881Speter svn_wc_notify_skip_conflicted, pool); 2431251881Speter 2432251881Speter return SVN_NO_ERROR; 2433251881Speter } 2434251881Speter else if (conflict_ignored) 2435251881Speter { 2436251881Speter db->shadowed = TRUE; 2437251881Speter } 2438251881Speter 2439251881Speter /* Is this path a fresh tree conflict victim? If so, skip the tree 2440251881Speter with one notification. */ 2441251881Speter 2442251881Speter /* Check for conflicts only when we haven't already recorded 2443251881Speter * a tree-conflict on a parent node. */ 2444251881Speter if (!db->shadowed) 2445251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath, 2446251881Speter status, TRUE, svn_node_dir, 2447251881Speter svn_wc_conflict_action_edit, 2448251881Speter db->pool, pool)); 2449251881Speter 2450251881Speter /* Remember the roots of any locally deleted trees. */ 2451251881Speter if (tree_conflict != NULL) 2452251881Speter { 2453251881Speter svn_wc_conflict_reason_t reason; 2454251881Speter db->edit_conflict = tree_conflict; 2455251881Speter /* Other modifications wouldn't be a tree conflict */ 2456251881Speter 2457251881Speter SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 2458251881Speter eb->db, db->local_abspath, 2459251881Speter tree_conflict, 2460251881Speter db->pool, db->pool)); 2461251881Speter SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted 2462251881Speter || reason == svn_wc_conflict_reason_moved_away 2463251881Speter || reason == svn_wc_conflict_reason_replaced 2464251881Speter || reason == svn_wc_conflict_reason_obstructed); 2465251881Speter 2466251881Speter /* Continue updating BASE */ 2467251881Speter if (reason == svn_wc_conflict_reason_obstructed) 2468251881Speter db->edit_obstructed = TRUE; 2469251881Speter else 2470251881Speter db->shadowed = TRUE; 2471251881Speter } 2472251881Speter 2473251881Speter /* Mark directory as being at target_revision and URL, but incomplete. */ 2474251881Speter SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath, 2475251881Speter db->new_relpath, 2476251881Speter *eb->target_revision, 2477251881Speter pool)); 2478251881Speter 2479251881Speter return SVN_NO_ERROR; 2480251881Speter} 2481251881Speter 2482251881Speter 2483251881Speter/* An svn_delta_editor_t function. */ 2484251881Speterstatic svn_error_t * 2485251881Speterchange_dir_prop(void *dir_baton, 2486251881Speter const char *name, 2487251881Speter const svn_string_t *value, 2488251881Speter apr_pool_t *pool) 2489251881Speter{ 2490251881Speter svn_prop_t *propchange; 2491251881Speter struct dir_baton *db = dir_baton; 2492251881Speter 2493251881Speter if (db->skip_this) 2494251881Speter return SVN_NO_ERROR; 2495251881Speter 2496251881Speter propchange = apr_array_push(db->propchanges); 2497251881Speter propchange->name = apr_pstrdup(db->pool, name); 2498251881Speter propchange->value = value ? svn_string_dup(value, db->pool) : NULL; 2499251881Speter 2500251881Speter if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind) 2501251881Speter SVN_ERR(mark_directory_edited(db, pool)); 2502251881Speter 2503251881Speter return SVN_NO_ERROR; 2504251881Speter} 2505251881Speter 2506251881Speter/* If any of the svn_prop_t objects in PROPCHANGES represents a change 2507251881Speter to the SVN_PROP_EXTERNALS property, return that change, else return 2508251881Speter null. If PROPCHANGES contains more than one such change, return 2509251881Speter the first. */ 2510251881Speterstatic const svn_prop_t * 2511251881Speterexternals_prop_changed(const apr_array_header_t *propchanges) 2512251881Speter{ 2513251881Speter int i; 2514251881Speter 2515251881Speter for (i = 0; i < propchanges->nelts; i++) 2516251881Speter { 2517251881Speter const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t)); 2518251881Speter if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0) 2519251881Speter return p; 2520251881Speter } 2521251881Speter 2522251881Speter return NULL; 2523251881Speter} 2524251881Speter 2525251881Speter 2526251881Speter 2527251881Speter/* An svn_delta_editor_t function. */ 2528251881Speterstatic svn_error_t * 2529251881Speterclose_directory(void *dir_baton, 2530251881Speter apr_pool_t *pool) 2531251881Speter{ 2532251881Speter struct dir_baton *db = dir_baton; 2533251881Speter struct edit_baton *eb = db->edit_baton; 2534251881Speter svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown; 2535251881Speter apr_array_header_t *entry_prop_changes; 2536251881Speter apr_array_header_t *dav_prop_changes; 2537251881Speter apr_array_header_t *regular_prop_changes; 2538251881Speter apr_hash_t *base_props; 2539251881Speter apr_hash_t *actual_props; 2540251881Speter apr_hash_t *new_base_props = NULL; 2541251881Speter apr_hash_t *new_actual_props = NULL; 2542251881Speter svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM; 2543251881Speter apr_time_t new_changed_date = 0; 2544251881Speter const char *new_changed_author = NULL; 2545251881Speter apr_pool_t *scratch_pool = db->pool; 2546251881Speter svn_skel_t *all_work_items = NULL; 2547251881Speter svn_skel_t *conflict_skel = NULL; 2548251881Speter 2549251881Speter /* Skip if we're in a conflicted tree. */ 2550251881Speter if (db->skip_this) 2551251881Speter { 2552251881Speter /* Allow the parent to complete its update. */ 2553251881Speter SVN_ERR(maybe_release_dir_info(db)); 2554251881Speter 2555251881Speter return SVN_NO_ERROR; 2556251881Speter } 2557251881Speter 2558251881Speter if (db->edited) 2559251881Speter conflict_skel = db->edit_conflict; 2560251881Speter 2561251881Speter SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes, 2562251881Speter &dav_prop_changes, ®ular_prop_changes, pool)); 2563251881Speter 2564251881Speter /* Fetch the existing properties. */ 2565251881Speter if ((!db->adding_dir || db->add_existed) 2566251881Speter && !db->shadowed) 2567251881Speter { 2568251881Speter SVN_ERR(svn_wc__get_actual_props(&actual_props, 2569251881Speter eb->db, db->local_abspath, 2570251881Speter scratch_pool, scratch_pool)); 2571251881Speter } 2572251881Speter else 2573251881Speter actual_props = apr_hash_make(pool); 2574251881Speter 2575251881Speter if (db->add_existed) 2576251881Speter { 2577251881Speter /* This node already exists. Grab the current pristine properties. */ 2578251881Speter SVN_ERR(svn_wc__db_read_pristine_props(&base_props, 2579251881Speter eb->db, db->local_abspath, 2580251881Speter scratch_pool, scratch_pool)); 2581251881Speter } 2582251881Speter else if (!db->adding_dir) 2583251881Speter { 2584251881Speter /* Get the BASE properties for proper merging. */ 2585251881Speter SVN_ERR(svn_wc__db_base_get_props(&base_props, 2586251881Speter eb->db, db->local_abspath, 2587251881Speter scratch_pool, scratch_pool)); 2588251881Speter } 2589251881Speter else 2590251881Speter base_props = apr_hash_make(pool); 2591251881Speter 2592251881Speter /* An incomplete directory might have props which were supposed to be 2593251881Speter deleted but weren't. Because the server sent us all the props we're 2594251881Speter supposed to have, any previous base props not in this list must be 2595251881Speter deleted (issue #1672). */ 2596251881Speter if (db->was_incomplete) 2597251881Speter { 2598251881Speter int i; 2599251881Speter apr_hash_t *props_to_delete; 2600251881Speter apr_hash_index_t *hi; 2601251881Speter 2602251881Speter /* In a copy of the BASE props, remove every property that we see an 2603251881Speter incoming change for. The remaining unmentioned properties are those 2604251881Speter which need to be deleted. */ 2605251881Speter props_to_delete = apr_hash_copy(pool, base_props); 2606251881Speter for (i = 0; i < regular_prop_changes->nelts; i++) 2607251881Speter { 2608251881Speter const svn_prop_t *prop; 2609251881Speter prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t); 2610251881Speter svn_hash_sets(props_to_delete, prop->name, NULL); 2611251881Speter } 2612251881Speter 2613251881Speter /* Add these props to the incoming propchanges (in 2614251881Speter * regular_prop_changes). */ 2615251881Speter for (hi = apr_hash_first(pool, props_to_delete); 2616251881Speter hi != NULL; 2617251881Speter hi = apr_hash_next(hi)) 2618251881Speter { 2619251881Speter const char *propname = svn__apr_hash_index_key(hi); 2620251881Speter svn_prop_t *prop = apr_array_push(regular_prop_changes); 2621251881Speter 2622251881Speter /* Record a deletion for PROPNAME. */ 2623251881Speter prop->name = propname; 2624251881Speter prop->value = NULL; 2625251881Speter } 2626251881Speter } 2627251881Speter 2628251881Speter /* If this directory has property changes stored up, now is the time 2629251881Speter to deal with them. */ 2630251881Speter if (regular_prop_changes->nelts) 2631251881Speter { 2632251881Speter /* If recording traversal info, then see if the 2633251881Speter SVN_PROP_EXTERNALS property on this directory changed, 2634251881Speter and record before and after for the change. */ 2635251881Speter if (eb->external_func) 2636251881Speter { 2637251881Speter const svn_prop_t *change 2638251881Speter = externals_prop_changed(regular_prop_changes); 2639251881Speter 2640251881Speter if (change) 2641251881Speter { 2642251881Speter const svn_string_t *new_val_s = change->value; 2643251881Speter const svn_string_t *old_val_s; 2644251881Speter 2645251881Speter old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS); 2646251881Speter 2647251881Speter if ((new_val_s == NULL) && (old_val_s == NULL)) 2648251881Speter ; /* No value before, no value after... so do nothing. */ 2649251881Speter else if (new_val_s && old_val_s 2650251881Speter && (svn_string_compare(old_val_s, new_val_s))) 2651251881Speter ; /* Value did not change... so do nothing. */ 2652251881Speter else if (old_val_s || new_val_s) 2653251881Speter /* something changed, record the change */ 2654251881Speter { 2655251881Speter SVN_ERR((eb->external_func)( 2656251881Speter eb->external_baton, 2657251881Speter db->local_abspath, 2658251881Speter old_val_s, 2659251881Speter new_val_s, 2660251881Speter db->ambient_depth, 2661251881Speter db->pool)); 2662251881Speter } 2663251881Speter } 2664251881Speter } 2665251881Speter 2666251881Speter if (db->shadowed) 2667251881Speter { 2668251881Speter /* We don't have a relevant actual row, but we need actual properties 2669251881Speter to allow property merging without conflicts. */ 2670251881Speter if (db->adding_dir) 2671251881Speter actual_props = apr_hash_make(scratch_pool); 2672251881Speter else 2673251881Speter actual_props = base_props; 2674251881Speter } 2675251881Speter 2676251881Speter /* Merge pending properties. */ 2677251881Speter new_base_props = svn_prop__patch(base_props, regular_prop_changes, 2678251881Speter db->pool); 2679251881Speter SVN_ERR_W(svn_wc__merge_props(&conflict_skel, 2680251881Speter &prop_state, 2681251881Speter &new_actual_props, 2682251881Speter eb->db, 2683251881Speter db->local_abspath, 2684251881Speter NULL /* use baseprops */, 2685251881Speter base_props, 2686251881Speter actual_props, 2687251881Speter regular_prop_changes, 2688251881Speter db->pool, 2689251881Speter scratch_pool), 2690251881Speter _("Couldn't do property merge")); 2691251881Speter /* After a (not-dry-run) merge, we ALWAYS have props to save. */ 2692251881Speter SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL); 2693251881Speter } 2694251881Speter 2695251881Speter SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date, 2696251881Speter &new_changed_author, entry_prop_changes, 2697251881Speter scratch_pool, scratch_pool)); 2698251881Speter 2699251881Speter /* Check if we should add some not-present markers before marking the 2700251881Speter directory complete (Issue #3569) */ 2701251881Speter { 2702251881Speter apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath); 2703251881Speter 2704251881Speter if (new_children != NULL) 2705251881Speter { 2706251881Speter apr_hash_index_t *hi; 2707251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2708251881Speter 2709251881Speter for (hi = apr_hash_first(scratch_pool, new_children); 2710251881Speter hi; 2711251881Speter hi = apr_hash_next(hi)) 2712251881Speter { 2713251881Speter const char *child_name; 2714251881Speter const char *child_abspath; 2715251881Speter const char *child_relpath; 2716251881Speter const svn_dirent_t *dirent; 2717251881Speter svn_wc__db_status_t status; 2718251881Speter svn_node_kind_t child_kind; 2719251881Speter svn_error_t *err; 2720251881Speter 2721251881Speter svn_pool_clear(iterpool); 2722251881Speter 2723251881Speter child_name = svn__apr_hash_index_key(hi); 2724251881Speter child_abspath = svn_dirent_join(db->local_abspath, child_name, 2725251881Speter iterpool); 2726251881Speter 2727251881Speter dirent = svn__apr_hash_index_val(hi); 2728251881Speter child_kind = (dirent->kind == svn_node_dir) 2729251881Speter ? svn_node_dir 2730251881Speter : svn_node_file; 2731251881Speter 2732251881Speter if (db->ambient_depth < svn_depth_immediates 2733251881Speter && child_kind == svn_node_dir) 2734251881Speter continue; /* We don't need the subdirs */ 2735251881Speter 2736251881Speter /* ### We just check if there is some node in BASE at this path */ 2737251881Speter err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, 2738251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2739251881Speter NULL, NULL, NULL, NULL, NULL, 2740251881Speter eb->db, child_abspath, 2741251881Speter iterpool, iterpool); 2742251881Speter 2743251881Speter if (!err) 2744251881Speter { 2745251881Speter svn_boolean_t is_wcroot; 2746251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath, 2747251881Speter iterpool)); 2748251881Speter 2749251881Speter if (!is_wcroot) 2750251881Speter continue; /* Everything ok... Nothing to do here */ 2751251881Speter /* Fall through to allow recovering later */ 2752251881Speter } 2753251881Speter else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2754251881Speter return svn_error_trace(err); 2755251881Speter 2756251881Speter svn_error_clear(err); 2757251881Speter 2758251881Speter child_relpath = svn_relpath_join(db->new_relpath, child_name, 2759251881Speter iterpool); 2760251881Speter 2761251881Speter SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, 2762251881Speter child_abspath, 2763251881Speter child_relpath, 2764251881Speter eb->repos_root, 2765251881Speter eb->repos_uuid, 2766251881Speter *eb->target_revision, 2767251881Speter child_kind, 2768251881Speter NULL, NULL, 2769251881Speter iterpool)); 2770251881Speter } 2771251881Speter 2772251881Speter svn_pool_destroy(iterpool); 2773251881Speter } 2774251881Speter } 2775251881Speter 2776251881Speter if (apr_hash_count(db->not_present_files)) 2777251881Speter { 2778251881Speter apr_hash_index_t *hi; 2779251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2780251881Speter 2781251881Speter /* This should call some new function (which could also be used 2782251881Speter for new_children above) to add all the names in single 2783251881Speter transaction, but I can't even trigger it. I've tried 2784251881Speter ra_local, ra_svn, ra_neon, ra_serf and they all call 2785251881Speter close_file before close_dir. */ 2786251881Speter for (hi = apr_hash_first(scratch_pool, db->not_present_files); 2787251881Speter hi; 2788251881Speter hi = apr_hash_next(hi)) 2789251881Speter { 2790251881Speter const char *child = svn__apr_hash_index_key(hi); 2791251881Speter const char *child_abspath, *child_relpath; 2792251881Speter 2793251881Speter svn_pool_clear(iterpool); 2794251881Speter 2795251881Speter child_abspath = svn_dirent_join(db->local_abspath, child, iterpool); 2796251881Speter child_relpath = svn_dirent_join(db->new_relpath, child, iterpool); 2797251881Speter 2798251881Speter SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, 2799251881Speter child_abspath, 2800251881Speter child_relpath, 2801251881Speter eb->repos_root, 2802251881Speter eb->repos_uuid, 2803251881Speter *eb->target_revision, 2804251881Speter svn_node_file, 2805251881Speter NULL, NULL, 2806251881Speter iterpool)); 2807251881Speter } 2808251881Speter svn_pool_destroy(iterpool); 2809251881Speter } 2810251881Speter 2811251881Speter /* If this directory is merely an anchor for a targeted child, then we 2812251881Speter should not be updating the node at all. */ 2813251881Speter if (db->parent_baton == NULL 2814251881Speter && *eb->target_basename != '\0') 2815251881Speter { 2816251881Speter /* And we should not have received any changes! */ 2817251881Speter SVN_ERR_ASSERT(db->propchanges->nelts == 0); 2818251881Speter /* ... which also implies NEW_CHANGED_* are not set, 2819251881Speter and NEW_BASE_PROPS == NULL. */ 2820251881Speter } 2821251881Speter else 2822251881Speter { 2823251881Speter apr_hash_t *props; 2824251881Speter apr_array_header_t *iprops = NULL; 2825251881Speter 2826251881Speter /* ### we know a base node already exists. it was created in 2827251881Speter ### open_directory or add_directory. let's just preserve the 2828251881Speter ### existing DEPTH value, and possibly CHANGED_*. */ 2829251881Speter /* If we received any changed_* values, then use them. */ 2830251881Speter if (SVN_IS_VALID_REVNUM(new_changed_rev)) 2831251881Speter db->changed_rev = new_changed_rev; 2832251881Speter if (new_changed_date != 0) 2833251881Speter db->changed_date = new_changed_date; 2834251881Speter if (new_changed_author != NULL) 2835251881Speter db->changed_author = new_changed_author; 2836251881Speter 2837251881Speter /* If no depth is set yet, set to infinity. */ 2838251881Speter if (db->ambient_depth == svn_depth_unknown) 2839251881Speter db->ambient_depth = svn_depth_infinity; 2840251881Speter 2841251881Speter if (eb->depth_is_sticky 2842251881Speter && db->ambient_depth != eb->requested_depth) 2843251881Speter { 2844251881Speter /* After a depth upgrade the entry must reflect the new depth. 2845251881Speter Upgrading to infinity changes the depth of *all* directories, 2846251881Speter upgrading to something else only changes the target. */ 2847251881Speter 2848251881Speter if (eb->requested_depth == svn_depth_infinity 2849251881Speter || (strcmp(db->local_abspath, eb->target_abspath) == 0 2850251881Speter && eb->requested_depth > db->ambient_depth)) 2851251881Speter { 2852251881Speter db->ambient_depth = eb->requested_depth; 2853251881Speter } 2854251881Speter } 2855251881Speter 2856251881Speter /* Do we have new properties to install? Or shall we simply retain 2857251881Speter the prior set of properties? If we're installing new properties, 2858251881Speter then we also want to write them to an old-style props file. */ 2859251881Speter props = new_base_props; 2860251881Speter if (props == NULL) 2861251881Speter props = base_props; 2862251881Speter 2863251881Speter if (conflict_skel) 2864251881Speter { 2865251881Speter svn_skel_t *work_item; 2866251881Speter 2867251881Speter SVN_ERR(complete_conflict(conflict_skel, 2868251881Speter db->edit_baton, 2869251881Speter db->local_abspath, 2870251881Speter db->old_repos_relpath, 2871251881Speter db->old_revision, 2872251881Speter db->new_relpath, 2873251881Speter svn_node_dir, svn_node_dir, 2874251881Speter db->pool, scratch_pool)); 2875251881Speter 2876251881Speter SVN_ERR(svn_wc__conflict_create_markers(&work_item, 2877251881Speter eb->db, db->local_abspath, 2878251881Speter conflict_skel, 2879251881Speter scratch_pool, scratch_pool)); 2880251881Speter 2881251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 2882251881Speter scratch_pool); 2883251881Speter } 2884251881Speter 2885251881Speter /* Any inherited props to be set set for this base node? */ 2886251881Speter if (eb->wcroot_iprops) 2887251881Speter { 2888251881Speter iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath); 2889251881Speter 2890251881Speter /* close_edit may also update iprops for switched nodes, catching 2891251881Speter those for which close_directory is never called (e.g. a switch 2892251881Speter with no changes). So as a minor optimization we remove any 2893251881Speter iprops from the hash so as not to set them again in 2894251881Speter close_edit. */ 2895251881Speter if (iprops) 2896251881Speter svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL); 2897251881Speter } 2898251881Speter 2899251881Speter /* Update the BASE data for the directory and mark the directory 2900251881Speter complete */ 2901251881Speter SVN_ERR(svn_wc__db_base_add_directory( 2902251881Speter eb->db, db->local_abspath, 2903251881Speter eb->wcroot_abspath, 2904251881Speter db->new_relpath, 2905251881Speter eb->repos_root, eb->repos_uuid, 2906251881Speter *eb->target_revision, 2907251881Speter props, 2908251881Speter db->changed_rev, db->changed_date, db->changed_author, 2909251881Speter NULL /* children */, 2910251881Speter db->ambient_depth, 2911251881Speter (dav_prop_changes->nelts > 0) 2912251881Speter ? svn_prop_array_to_hash(dav_prop_changes, pool) 2913251881Speter : NULL, 2914251881Speter conflict_skel, 2915251881Speter (! db->shadowed) && new_base_props != NULL, 2916251881Speter new_actual_props, 2917251881Speter iprops, all_work_items, 2918251881Speter scratch_pool)); 2919251881Speter } 2920251881Speter 2921251881Speter /* Process all of the queued work items for this directory. */ 2922251881Speter SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath, 2923251881Speter eb->cancel_func, eb->cancel_baton, 2924251881Speter scratch_pool)); 2925251881Speter 2926251881Speter if (conflict_skel && eb->conflict_func) 2927251881Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, 2928251881Speter conflict_skel, 2929251881Speter NULL /* merge_options */, 2930251881Speter eb->conflict_func, 2931251881Speter eb->conflict_baton, 2932251881Speter eb->cancel_func, 2933253734Speter eb->cancel_baton, 2934251881Speter scratch_pool)); 2935251881Speter 2936251881Speter /* Notify of any prop changes on this directory -- but do nothing if 2937251881Speter it's an added or skipped directory, because notification has already 2938251881Speter happened in that case - unless the add was obstructed by a dir 2939251881Speter scheduled for addition without history, in which case we handle 2940251881Speter notification here). */ 2941251881Speter if (!db->already_notified && eb->notify_func && db->edited) 2942251881Speter { 2943251881Speter svn_wc_notify_t *notify; 2944251881Speter svn_wc_notify_action_t action; 2945251881Speter 2946251881Speter if (db->shadowed || db->edit_obstructed) 2947251881Speter action = svn_wc_notify_update_shadowed_update; 2948251881Speter else if (db->obstruction_found || db->add_existed) 2949251881Speter action = svn_wc_notify_exists; 2950251881Speter else 2951251881Speter action = svn_wc_notify_update_update; 2952251881Speter 2953251881Speter notify = svn_wc_create_notify(db->local_abspath, action, pool); 2954251881Speter notify->kind = svn_node_dir; 2955251881Speter notify->prop_state = prop_state; 2956251881Speter notify->revision = *eb->target_revision; 2957251881Speter notify->old_revision = db->old_revision; 2958251881Speter 2959251881Speter eb->notify_func(eb->notify_baton, notify, scratch_pool); 2960251881Speter } 2961251881Speter 2962251881Speter /* We're done with this directory, so remove one reference from the 2963251881Speter bump information. */ 2964251881Speter SVN_ERR(maybe_release_dir_info(db)); 2965251881Speter 2966251881Speter return SVN_NO_ERROR; 2967251881Speter} 2968251881Speter 2969251881Speter 2970251881Speter/* Common code for 'absent_file' and 'absent_directory'. */ 2971251881Speterstatic svn_error_t * 2972251881Speterabsent_node(const char *path, 2973251881Speter svn_node_kind_t absent_kind, 2974251881Speter void *parent_baton, 2975251881Speter apr_pool_t *pool) 2976251881Speter{ 2977251881Speter struct dir_baton *pb = parent_baton; 2978251881Speter struct edit_baton *eb = pb->edit_baton; 2979251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 2980251881Speter const char *name = svn_dirent_basename(path, NULL); 2981251881Speter const char *local_abspath; 2982251881Speter svn_error_t *err; 2983251881Speter svn_wc__db_status_t status; 2984251881Speter svn_node_kind_t kind; 2985251881Speter 2986251881Speter if (pb->skip_this) 2987251881Speter return SVN_NO_ERROR; 2988251881Speter 2989251881Speter SVN_ERR(mark_directory_edited(pb, scratch_pool)); 2990251881Speter 2991251881Speter local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool); 2992251881Speter 2993251881Speter /* If an item by this name is scheduled for addition that's a 2994251881Speter genuine tree-conflict. */ 2995251881Speter err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 2996251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2997251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2998251881Speter NULL, NULL, NULL, NULL, 2999251881Speter eb->db, local_abspath, 3000251881Speter scratch_pool, scratch_pool); 3001251881Speter 3002251881Speter if (err) 3003251881Speter { 3004251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 3005251881Speter return svn_error_trace(err); 3006251881Speter 3007251881Speter svn_error_clear(err); 3008251881Speter status = svn_wc__db_status_not_present; 3009251881Speter kind = svn_node_unknown; 3010251881Speter } 3011251881Speter 3012251881Speter if (status == svn_wc__db_status_normal 3013251881Speter && kind == svn_node_dir) 3014251881Speter { 3015251881Speter /* We found an obstructing working copy! 3016251881Speter 3017251881Speter We can do two things now: 3018251881Speter 1) notify the user, record a skip, etc. 3019251881Speter 2) Just record the absent node in BASE in the parent 3020251881Speter working copy. 3021251881Speter 3022251881Speter As option 2 happens to be exactly what we do anyway, lets do that. 3023251881Speter */ 3024251881Speter } 3025251881Speter else if (status == svn_wc__db_status_not_present 3026251881Speter || status == svn_wc__db_status_server_excluded 3027251881Speter || status == svn_wc__db_status_excluded) 3028251881Speter { 3029251881Speter /* The BASE node is not actually there, so we can safely turn it into 3030251881Speter an absent node */ 3031251881Speter } 3032251881Speter else 3033251881Speter { 3034251881Speter /* We have a local addition. If this would be a BASE node it would have 3035251881Speter been deleted before we get here. (Which might have turned it into 3036251881Speter a copy). 3037251881Speter 3038251881Speter ### This should be recorded as a tree conflict and the update 3039251881Speter ### can just continue, as we can just record the absent status 3040251881Speter ### in BASE. 3041251881Speter */ 3042251881Speter SVN_ERR_ASSERT(status != svn_wc__db_status_normal); 3043251881Speter 3044251881Speter return svn_error_createf( 3045251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 3046251881Speter _("Failed to mark '%s' absent: item of the same name is already " 3047251881Speter "scheduled for addition"), 3048251881Speter svn_dirent_local_style(local_abspath, pool)); 3049251881Speter } 3050251881Speter 3051251881Speter { 3052251881Speter const char *repos_relpath; 3053251881Speter repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool); 3054251881Speter 3055251881Speter /* Insert an excluded node below the parent node to note that this child 3056251881Speter is absent. (This puts it in the parent db if the child is obstructed) */ 3057251881Speter SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath, 3058251881Speter repos_relpath, eb->repos_root, 3059251881Speter eb->repos_uuid, 3060251881Speter *(eb->target_revision), 3061251881Speter absent_kind, 3062251881Speter svn_wc__db_status_server_excluded, 3063251881Speter NULL, NULL, 3064251881Speter scratch_pool)); 3065251881Speter } 3066251881Speter 3067251881Speter svn_pool_destroy(scratch_pool); 3068251881Speter 3069251881Speter return SVN_NO_ERROR; 3070251881Speter} 3071251881Speter 3072251881Speter 3073251881Speter/* An svn_delta_editor_t function. */ 3074251881Speterstatic svn_error_t * 3075251881Speterabsent_file(const char *path, 3076251881Speter void *parent_baton, 3077251881Speter apr_pool_t *pool) 3078251881Speter{ 3079251881Speter return absent_node(path, svn_node_file, parent_baton, pool); 3080251881Speter} 3081251881Speter 3082251881Speter 3083251881Speter/* An svn_delta_editor_t function. */ 3084251881Speterstatic svn_error_t * 3085251881Speterabsent_directory(const char *path, 3086251881Speter void *parent_baton, 3087251881Speter apr_pool_t *pool) 3088251881Speter{ 3089251881Speter return absent_node(path, svn_node_dir, parent_baton, pool); 3090251881Speter} 3091251881Speter 3092251881Speter 3093251881Speter/* An svn_delta_editor_t function. */ 3094251881Speterstatic svn_error_t * 3095251881Speteradd_file(const char *path, 3096251881Speter void *parent_baton, 3097251881Speter const char *copyfrom_path, 3098251881Speter svn_revnum_t copyfrom_rev, 3099251881Speter apr_pool_t *pool, 3100251881Speter void **file_baton) 3101251881Speter{ 3102251881Speter struct dir_baton *pb = parent_baton; 3103251881Speter struct edit_baton *eb = pb->edit_baton; 3104251881Speter struct file_baton *fb; 3105251881Speter svn_node_kind_t kind = svn_node_none; 3106251881Speter svn_node_kind_t wc_kind = svn_node_unknown; 3107251881Speter svn_wc__db_status_t status = svn_wc__db_status_normal; 3108251881Speter apr_pool_t *scratch_pool; 3109251881Speter svn_boolean_t conflicted = FALSE; 3110251881Speter svn_boolean_t conflict_ignored = FALSE; 3111251881Speter svn_boolean_t versioned_locally_and_present = FALSE; 3112251881Speter svn_skel_t *tree_conflict = NULL; 3113251881Speter svn_error_t *err = SVN_NO_ERROR; 3114251881Speter 3115251881Speter SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev))); 3116251881Speter 3117251881Speter SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool)); 3118251881Speter *file_baton = fb; 3119251881Speter 3120251881Speter if (fb->skip_this) 3121251881Speter return SVN_NO_ERROR; 3122251881Speter 3123251881Speter SVN_ERR(mark_file_edited(fb, pool)); 3124251881Speter 3125251881Speter /* The file_pool can stick around for a *long* time, so we want to 3126251881Speter use a subpool for any temporary allocations. */ 3127251881Speter scratch_pool = svn_pool_create(pool); 3128251881Speter 3129251881Speter 3130251881Speter /* It may not be named the same as the administrative directory. */ 3131251881Speter if (svn_wc_is_adm_dir(fb->name, pool)) 3132251881Speter return svn_error_createf( 3133251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 3134251881Speter _("Failed to add file '%s': object of the same name as the " 3135251881Speter "administrative directory"), 3136251881Speter svn_dirent_local_style(fb->local_abspath, pool)); 3137251881Speter 3138251881Speter if (!eb->clean_checkout) 3139251881Speter { 3140251881Speter SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool)); 3141251881Speter 3142251881Speter err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, 3143251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3144251881Speter NULL, NULL, NULL, NULL, NULL, 3145251881Speter &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, 3146251881Speter eb->db, fb->local_abspath, 3147251881Speter scratch_pool, scratch_pool); 3148251881Speter } 3149251881Speter 3150251881Speter if (err) 3151251881Speter { 3152251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 3153251881Speter return svn_error_trace(err); 3154251881Speter 3155251881Speter svn_error_clear(err); 3156251881Speter wc_kind = svn_node_unknown; 3157251881Speter conflicted = FALSE; 3158251881Speter 3159251881Speter versioned_locally_and_present = FALSE; 3160251881Speter } 3161251881Speter else if (wc_kind == svn_node_dir 3162251881Speter && status == svn_wc__db_status_normal) 3163251881Speter { 3164251881Speter /* !! We found the root of a separate working copy obstructing the wc !! 3165251881Speter 3166251881Speter If the directory would be part of our own working copy then 3167251881Speter we wouldn't have been called as an add_file(). 3168251881Speter 3169251881Speter The only thing we can do is add a not-present node, to allow 3170251881Speter a future update to bring in the new files when the problem is 3171251881Speter resolved. */ 3172251881Speter svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), 3173251881Speter (void *)1); 3174251881Speter 3175251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3176251881Speter fb->skip_this = TRUE; 3177251881Speter fb->already_notified = TRUE; 3178251881Speter 3179251881Speter do_notification(eb, fb->local_abspath, svn_node_file, 3180251881Speter svn_wc_notify_update_skip_obstruction, scratch_pool); 3181251881Speter 3182251881Speter svn_pool_destroy(scratch_pool); 3183251881Speter 3184251881Speter return SVN_NO_ERROR; 3185251881Speter } 3186251881Speter else if (status == svn_wc__db_status_normal 3187251881Speter && (wc_kind == svn_node_file 3188251881Speter || wc_kind == svn_node_symlink)) 3189251881Speter { 3190251881Speter /* We found a file external occupating the place we need in BASE. 3191251881Speter 3192251881Speter We can't add a not-present node in this case as that would overwrite 3193251881Speter the file external. Luckily the file external itself stops us from 3194251881Speter forgetting a child of this parent directory like an obstructing 3195251881Speter working copy would. 3196251881Speter 3197251881Speter The reason we get here is that the adm crawler doesn't report 3198251881Speter file externals. 3199251881Speter */ 3200251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3201251881Speter fb->skip_this = TRUE; 3202251881Speter fb->already_notified = TRUE; 3203251881Speter 3204251881Speter do_notification(eb, fb->local_abspath, svn_node_file, 3205251881Speter svn_wc_notify_update_skip_obstruction, scratch_pool); 3206251881Speter 3207251881Speter svn_pool_destroy(scratch_pool); 3208251881Speter 3209251881Speter return SVN_NO_ERROR; 3210251881Speter } 3211251881Speter else if (wc_kind == svn_node_unknown) 3212251881Speter versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ 3213251881Speter else 3214251881Speter versioned_locally_and_present = IS_NODE_PRESENT(status); 3215251881Speter 3216251881Speter 3217251881Speter /* Is this path a conflict victim? */ 3218251881Speter if (fb->shadowed) 3219251881Speter conflicted = FALSE; /* Conflict applies to WORKING */ 3220251881Speter else if (conflicted) 3221251881Speter { 3222251881Speter if (pb->deletion_conflicts) 3223251881Speter tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name); 3224251881Speter 3225251881Speter if (tree_conflict) 3226251881Speter { 3227251881Speter svn_wc_conflict_reason_t reason; 3228251881Speter /* So this deletion wasn't just a deletion, it is actually a 3229251881Speter replacement. Let's install a better tree conflict. */ 3230251881Speter 3231251881Speter /* ### Should store the conflict in DB to allow reinstalling 3232251881Speter ### with theoretically more data in close_directory() */ 3233251881Speter 3234251881Speter SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 3235251881Speter eb->db, 3236251881Speter fb->local_abspath, 3237251881Speter tree_conflict, 3238251881Speter fb->pool, fb->pool)); 3239251881Speter 3240251881Speter tree_conflict = svn_wc__conflict_skel_create(fb->pool); 3241251881Speter 3242251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3243251881Speter tree_conflict, 3244251881Speter eb->db, fb->local_abspath, 3245251881Speter reason, svn_wc_conflict_action_replace, 3246251881Speter NULL, 3247251881Speter fb->pool, fb->pool)); 3248251881Speter 3249251881Speter /* And now stop checking for conflicts here and just perform 3250251881Speter a shadowed update */ 3251251881Speter fb->edit_conflict = tree_conflict; /* Cache for close_file */ 3252251881Speter tree_conflict = NULL; /* No direct notification */ 3253251881Speter fb->shadowed = TRUE; /* Just continue */ 3254251881Speter conflicted = FALSE; /* No skip */ 3255251881Speter } 3256251881Speter else 3257251881Speter SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 3258251881Speter eb->db, fb->local_abspath, pool)); 3259251881Speter } 3260251881Speter 3261251881Speter /* Now the usual conflict handling: skip. */ 3262251881Speter if (conflicted) 3263251881Speter { 3264251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3265251881Speter 3266251881Speter fb->skip_this = TRUE; 3267251881Speter fb->already_notified = TRUE; 3268251881Speter 3269251881Speter /* We skip this node, but once the update completes the parent node will 3270251881Speter be updated to the new revision. So a future recursive update of the 3271251881Speter parent will not bring in this new node as the revision of the parent 3272251881Speter describes to the repository that all children are available. 3273251881Speter 3274251881Speter To resolve this problem, we add a not-present node to allow bringing 3275251881Speter the node in once this conflict is resolved. 3276251881Speter 3277251881Speter Note that we can safely assume that no present base node exists, 3278251881Speter because then we would not have received an add_file. 3279251881Speter */ 3280251881Speter svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), 3281251881Speter (void *)1); 3282251881Speter 3283251881Speter do_notification(eb, fb->local_abspath, svn_node_unknown, 3284251881Speter svn_wc_notify_skip_conflicted, scratch_pool); 3285251881Speter 3286251881Speter svn_pool_destroy(scratch_pool); 3287251881Speter 3288251881Speter return SVN_NO_ERROR; 3289251881Speter } 3290251881Speter else if (conflict_ignored) 3291251881Speter { 3292251881Speter fb->shadowed = TRUE; 3293251881Speter } 3294251881Speter 3295251881Speter if (fb->shadowed) 3296251881Speter { 3297251881Speter /* Nothing to check; does not and will not exist in working copy */ 3298251881Speter } 3299251881Speter else if (versioned_locally_and_present) 3300251881Speter { 3301251881Speter /* What to do with a versioned or schedule-add file: 3302251881Speter 3303251881Speter If the UUID doesn't match the parent's, or the URL isn't a child of 3304251881Speter the parent dir's URL, it's an error. 3305251881Speter 3306251881Speter Set add_existed so that user notification is delayed until after any 3307251881Speter text or prop conflicts have been found. 3308251881Speter 3309251881Speter Whether the incoming add is a symlink or a file will only be known in 3310251881Speter close_file(), when the props are known. So with a locally added file 3311251881Speter or symlink, let close_file() check for a tree conflict. 3312251881Speter 3313251881Speter We will never see missing files here, because these would be 3314251881Speter re-added during the crawler phase. */ 3315251881Speter svn_boolean_t local_is_file; 3316251881Speter 3317251881Speter /* Is the local node a copy or move */ 3318251881Speter if (status == svn_wc__db_status_added) 3319251881Speter SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL, 3320251881Speter NULL, NULL, NULL, 3321251881Speter eb->db, fb->local_abspath, 3322251881Speter scratch_pool, scratch_pool)); 3323251881Speter 3324251881Speter /* Is there something that is a file? */ 3325251881Speter local_is_file = (wc_kind == svn_node_file 3326251881Speter || wc_kind == svn_node_symlink); 3327251881Speter 3328251881Speter /* Do tree conflict checking if 3329251881Speter * - if there is a local copy. 3330251881Speter * - if this is a switch operation 3331251881Speter * - the node kinds mismatch 3332251881Speter * 3333251881Speter * During switch, local adds at the same path as incoming adds get 3334251881Speter * "lost" in that switching back to the original will no longer have the 3335251881Speter * local add. So switch always alerts the user with a tree conflict. */ 3336251881Speter if (!eb->adds_as_modification 3337251881Speter || !local_is_file 3338251881Speter || status != svn_wc__db_status_added) 3339251881Speter { 3340251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, 3341251881Speter fb->local_abspath, 3342251881Speter status, FALSE, svn_node_none, 3343251881Speter svn_wc_conflict_action_add, 3344251881Speter scratch_pool, scratch_pool)); 3345251881Speter } 3346251881Speter 3347251881Speter if (tree_conflict == NULL) 3348251881Speter fb->add_existed = TRUE; /* Take over WORKING */ 3349251881Speter else 3350251881Speter fb->shadowed = TRUE; /* Only update BASE */ 3351251881Speter 3352251881Speter } 3353251881Speter else if (kind != svn_node_none) 3354251881Speter { 3355251881Speter /* There's an unversioned node at this path. */ 3356251881Speter fb->obstruction_found = TRUE; 3357251881Speter 3358251881Speter /* Unversioned, obstructing files are handled by text merge/conflict, 3359251881Speter * if unversioned obstructions are allowed. */ 3360251881Speter if (! (kind == svn_node_file && eb->allow_unver_obstructions)) 3361251881Speter { 3362251881Speter /* Bring in the node as deleted */ /* ### Obstructed Conflict */ 3363251881Speter fb->shadowed = TRUE; 3364251881Speter 3365251881Speter /* Mark a conflict */ 3366251881Speter tree_conflict = svn_wc__conflict_skel_create(fb->pool); 3367251881Speter 3368251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3369251881Speter tree_conflict, 3370251881Speter eb->db, fb->local_abspath, 3371251881Speter svn_wc_conflict_reason_unversioned, 3372251881Speter svn_wc_conflict_action_add, 3373251881Speter NULL, 3374251881Speter fb->pool, scratch_pool)); 3375251881Speter } 3376251881Speter } 3377251881Speter 3378251881Speter /* When this is not the update target add a not-present BASE node now, 3379251881Speter to allow marking the parent directory complete in its close_edit() call. 3380251881Speter This resolves issues when that occurs before the close_file(). */ 3381251881Speter if (pb->parent_baton 3382251881Speter || *eb->target_basename == '\0' 3383251881Speter || (strcmp(fb->local_abspath, eb->target_abspath) != 0)) 3384251881Speter { 3385251881Speter svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name), 3386251881Speter (void *)1); 3387251881Speter } 3388251881Speter 3389251881Speter if (tree_conflict != NULL) 3390251881Speter { 3391251881Speter SVN_ERR(complete_conflict(tree_conflict, 3392251881Speter fb->edit_baton, 3393251881Speter fb->local_abspath, 3394251881Speter fb->old_repos_relpath, 3395251881Speter fb->old_revision, 3396251881Speter fb->new_relpath, 3397251881Speter wc_kind, 3398251881Speter svn_node_file, 3399251881Speter fb->pool, scratch_pool)); 3400251881Speter 3401251881Speter SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, 3402251881Speter fb->local_abspath, 3403251881Speter tree_conflict, NULL, 3404251881Speter scratch_pool)); 3405251881Speter 3406253734Speter if (eb->conflict_func) 3407253734Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, 3408253734Speter tree_conflict, 3409253734Speter NULL /* merge_options */, 3410253734Speter eb->conflict_func, 3411253734Speter eb->conflict_baton, 3412253734Speter eb->cancel_func, 3413253734Speter eb->cancel_baton, 3414253734Speter scratch_pool)); 3415253734Speter 3416251881Speter fb->already_notified = TRUE; 3417251881Speter do_notification(eb, fb->local_abspath, svn_node_file, 3418251881Speter svn_wc_notify_tree_conflict, scratch_pool); 3419251881Speter } 3420251881Speter 3421251881Speter svn_pool_destroy(scratch_pool); 3422251881Speter 3423251881Speter return SVN_NO_ERROR; 3424251881Speter} 3425251881Speter 3426251881Speter 3427251881Speter/* An svn_delta_editor_t function. */ 3428251881Speterstatic svn_error_t * 3429251881Speteropen_file(const char *path, 3430251881Speter void *parent_baton, 3431251881Speter svn_revnum_t base_revision, 3432251881Speter apr_pool_t *pool, 3433251881Speter void **file_baton) 3434251881Speter{ 3435251881Speter struct dir_baton *pb = parent_baton; 3436251881Speter struct edit_baton *eb = pb->edit_baton; 3437251881Speter struct file_baton *fb; 3438251881Speter svn_boolean_t conflicted; 3439251881Speter svn_boolean_t conflict_ignored = FALSE; 3440251881Speter svn_boolean_t have_work; 3441251881Speter svn_wc__db_status_t status; 3442251881Speter svn_node_kind_t wc_kind; 3443251881Speter svn_skel_t *tree_conflict = NULL; 3444251881Speter 3445251881Speter /* the file_pool can stick around for a *long* time, so we want to use 3446251881Speter a subpool for any temporary allocations. */ 3447251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 3448251881Speter 3449251881Speter SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool)); 3450251881Speter *file_baton = fb; 3451251881Speter 3452251881Speter if (fb->skip_this) 3453251881Speter return SVN_NO_ERROR; 3454251881Speter 3455251881Speter /* Detect obstructing working copies */ 3456251881Speter { 3457251881Speter svn_boolean_t is_root; 3458251881Speter 3459251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath, 3460251881Speter pool)); 3461251881Speter 3462251881Speter if (is_root) 3463251881Speter { 3464251881Speter /* Just skip this node; a future update will handle it */ 3465251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3466251881Speter fb->skip_this = TRUE; 3467251881Speter fb->already_notified = TRUE; 3468251881Speter 3469251881Speter do_notification(eb, fb->local_abspath, svn_node_file, 3470251881Speter svn_wc_notify_update_skip_obstruction, pool); 3471251881Speter 3472251881Speter return SVN_NO_ERROR; 3473251881Speter } 3474251881Speter } 3475251881Speter 3476251881Speter /* Sanity check. */ 3477251881Speter 3478251881Speter /* If replacing, make sure the .svn entry already exists. */ 3479251881Speter SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision, 3480251881Speter &fb->old_repos_relpath, NULL, NULL, 3481251881Speter &fb->changed_rev, &fb->changed_date, 3482251881Speter &fb->changed_author, NULL, 3483251881Speter &fb->original_checksum, NULL, NULL, NULL, 3484251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 3485251881Speter &conflicted, NULL, NULL, &fb->local_prop_mods, 3486251881Speter NULL, NULL, &have_work, 3487251881Speter eb->db, fb->local_abspath, 3488251881Speter fb->pool, scratch_pool)); 3489251881Speter 3490251881Speter if (have_work) 3491251881Speter SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision, 3492251881Speter &fb->old_repos_relpath, NULL, NULL, 3493251881Speter &fb->changed_rev, &fb->changed_date, 3494251881Speter &fb->changed_author, NULL, 3495251881Speter &fb->original_checksum, NULL, NULL, 3496251881Speter NULL, NULL, NULL, 3497251881Speter eb->db, fb->local_abspath, 3498251881Speter fb->pool, scratch_pool)); 3499251881Speter 3500251881Speter /* Is this path a conflict victim? */ 3501251881Speter if (fb->shadowed) 3502251881Speter conflicted = FALSE; /* Conflict applies to WORKING */ 3503251881Speter else if (conflicted) 3504251881Speter SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 3505251881Speter eb->db, fb->local_abspath, pool)); 3506251881Speter if (conflicted) 3507251881Speter { 3508251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3509251881Speter 3510251881Speter fb->skip_this = TRUE; 3511251881Speter fb->already_notified = TRUE; 3512251881Speter 3513251881Speter do_notification(eb, fb->local_abspath, svn_node_unknown, 3514251881Speter svn_wc_notify_skip_conflicted, scratch_pool); 3515251881Speter 3516251881Speter svn_pool_destroy(scratch_pool); 3517251881Speter 3518251881Speter return SVN_NO_ERROR; 3519251881Speter } 3520251881Speter else if (conflict_ignored) 3521251881Speter { 3522251881Speter fb->shadowed = TRUE; 3523251881Speter } 3524251881Speter 3525251881Speter /* Check for conflicts only when we haven't already recorded 3526251881Speter * a tree-conflict on a parent node. */ 3527251881Speter if (!fb->shadowed) 3528251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath, 3529251881Speter status, TRUE, svn_node_file, 3530251881Speter svn_wc_conflict_action_edit, 3531251881Speter fb->pool, scratch_pool)); 3532251881Speter 3533251881Speter /* Is this path the victim of a newly-discovered tree conflict? */ 3534251881Speter if (tree_conflict != NULL) 3535251881Speter { 3536251881Speter svn_wc_conflict_reason_t reason; 3537251881Speter fb->edit_conflict = tree_conflict; 3538251881Speter /* Other modifications wouldn't be a tree conflict */ 3539251881Speter 3540251881Speter SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, 3541251881Speter eb->db, fb->local_abspath, 3542251881Speter tree_conflict, 3543251881Speter scratch_pool, scratch_pool)); 3544251881Speter SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted 3545251881Speter || reason == svn_wc_conflict_reason_moved_away 3546251881Speter || reason == svn_wc_conflict_reason_replaced 3547251881Speter || reason == svn_wc_conflict_reason_obstructed); 3548251881Speter 3549251881Speter /* Continue updating BASE */ 3550251881Speter if (reason == svn_wc_conflict_reason_obstructed) 3551251881Speter fb->edit_obstructed = TRUE; 3552251881Speter else 3553251881Speter fb->shadowed = TRUE; 3554251881Speter } 3555251881Speter 3556251881Speter svn_pool_destroy(scratch_pool); 3557251881Speter 3558251881Speter return SVN_NO_ERROR; 3559251881Speter} 3560251881Speter 3561251881Speter/* Implements svn_stream_lazyopen_func_t. */ 3562251881Speterstatic svn_error_t * 3563251881Speterlazy_open_source(svn_stream_t **stream, 3564251881Speter void *baton, 3565251881Speter apr_pool_t *result_pool, 3566251881Speter apr_pool_t *scratch_pool) 3567251881Speter{ 3568251881Speter struct file_baton *fb = baton; 3569251881Speter 3570251881Speter SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db, 3571251881Speter fb->local_abspath, 3572251881Speter fb->original_checksum, 3573251881Speter result_pool, scratch_pool)); 3574251881Speter 3575251881Speter 3576251881Speter return SVN_NO_ERROR; 3577251881Speter} 3578251881Speter 3579251881Speterstruct lazy_target_baton { 3580251881Speter struct file_baton *fb; 3581251881Speter struct handler_baton *hb; 3582251881Speter struct edit_baton *eb; 3583251881Speter}; 3584251881Speter 3585251881Speter/* Implements svn_stream_lazyopen_func_t. */ 3586251881Speterstatic svn_error_t * 3587251881Speterlazy_open_target(svn_stream_t **stream, 3588251881Speter void *baton, 3589251881Speter apr_pool_t *result_pool, 3590251881Speter apr_pool_t *scratch_pool) 3591251881Speter{ 3592251881Speter struct lazy_target_baton *tb = baton; 3593251881Speter 3594251881Speter SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath, 3595251881Speter NULL, &tb->hb->new_text_base_sha1_checksum, 3596251881Speter tb->fb->edit_baton->db, 3597251881Speter tb->eb->wcroot_abspath, 3598251881Speter result_pool, scratch_pool)); 3599251881Speter 3600251881Speter return SVN_NO_ERROR; 3601251881Speter} 3602251881Speter 3603251881Speter/* An svn_delta_editor_t function. */ 3604251881Speterstatic svn_error_t * 3605251881Speterapply_textdelta(void *file_baton, 3606251881Speter const char *expected_checksum, 3607251881Speter apr_pool_t *pool, 3608251881Speter svn_txdelta_window_handler_t *handler, 3609251881Speter void **handler_baton) 3610251881Speter{ 3611251881Speter struct file_baton *fb = file_baton; 3612251881Speter apr_pool_t *handler_pool = svn_pool_create(fb->pool); 3613251881Speter struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb)); 3614251881Speter struct edit_baton *eb = fb->edit_baton; 3615251881Speter const svn_checksum_t *recorded_base_checksum; 3616251881Speter svn_checksum_t *expected_base_checksum; 3617251881Speter svn_stream_t *source; 3618251881Speter struct lazy_target_baton *tb; 3619251881Speter svn_stream_t *target; 3620251881Speter 3621251881Speter if (fb->skip_this) 3622251881Speter { 3623251881Speter *handler = svn_delta_noop_window_handler; 3624251881Speter *handler_baton = NULL; 3625251881Speter return SVN_NO_ERROR; 3626251881Speter } 3627251881Speter 3628251881Speter SVN_ERR(mark_file_edited(fb, pool)); 3629251881Speter 3630251881Speter /* Parse checksum or sets expected_base_checksum to NULL */ 3631251881Speter SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5, 3632251881Speter expected_checksum, pool)); 3633251881Speter 3634251881Speter /* Before applying incoming svndiff data to text base, make sure 3635251881Speter text base hasn't been corrupted, and that its checksum 3636251881Speter matches the expected base checksum. */ 3637251881Speter 3638251881Speter /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and 3639251881Speter check our RECORDED_BASE_CHECKSUM. (In WC-1, we could not do this test 3640251881Speter for replaced nodes because we didn't store the checksum of the "revert 3641251881Speter base". In WC-NG, we do and we can.) */ 3642251881Speter recorded_base_checksum = fb->original_checksum; 3643251881Speter 3644251881Speter /* If we have a checksum that we want to compare to a MD5 checksum, 3645251881Speter ensure that it is a MD5 checksum */ 3646251881Speter if (recorded_base_checksum 3647251881Speter && expected_base_checksum 3648251881Speter && recorded_base_checksum->kind != svn_checksum_md5) 3649251881Speter SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum, 3650251881Speter eb->db, eb->wcroot_abspath, 3651251881Speter recorded_base_checksum, pool, pool)); 3652251881Speter 3653251881Speter 3654251881Speter if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum)) 3655251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL, 3656251881Speter _("Checksum mismatch for '%s':\n" 3657251881Speter " expected: %s\n" 3658251881Speter " recorded: %s\n"), 3659251881Speter svn_dirent_local_style(fb->local_abspath, pool), 3660251881Speter svn_checksum_to_cstring_display(expected_base_checksum, 3661251881Speter pool), 3662251881Speter svn_checksum_to_cstring_display(recorded_base_checksum, 3663251881Speter pool)); 3664251881Speter 3665251881Speter /* Open the text base for reading, unless this is an added file. */ 3666251881Speter 3667251881Speter /* 3668251881Speter kff todo: what we really need to do here is: 3669251881Speter 3670251881Speter 1. See if there's a file or dir by this name already here. 3671251881Speter 2. See if it's under revision control. 3672251881Speter 3. If both are true, open text-base. 3673251881Speter 4. If only 1 is true, bail, because we can't go destroying user's 3674251881Speter files (or as an alternative to bailing, move it to some tmp 3675251881Speter name and somehow tell the user, but communicating with the 3676251881Speter user without erroring is a whole callback system we haven't 3677251881Speter finished inventing yet.) 3678251881Speter */ 3679251881Speter 3680251881Speter if (! fb->adding_file) 3681251881Speter { 3682251881Speter SVN_ERR_ASSERT(!fb->original_checksum 3683251881Speter || fb->original_checksum->kind == svn_checksum_sha1); 3684251881Speter 3685251881Speter source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE, 3686251881Speter handler_pool); 3687251881Speter } 3688251881Speter else 3689251881Speter { 3690251881Speter source = svn_stream_empty(handler_pool); 3691251881Speter } 3692251881Speter 3693251881Speter /* If we don't have a recorded checksum, use the ra provided checksum */ 3694251881Speter if (!recorded_base_checksum) 3695251881Speter recorded_base_checksum = expected_base_checksum; 3696251881Speter 3697251881Speter /* Checksum the text base while applying deltas */ 3698251881Speter if (recorded_base_checksum) 3699251881Speter { 3700251881Speter hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum, 3701251881Speter handler_pool); 3702251881Speter 3703251881Speter /* Wrap stream and store reference to allow calculating the 3704251881Speter checksum. */ 3705251881Speter source = svn_stream_checksummed2(source, 3706251881Speter &hb->actual_source_checksum, 3707251881Speter NULL, recorded_base_checksum->kind, 3708251881Speter TRUE, handler_pool); 3709251881Speter hb->source_checksum_stream = source; 3710251881Speter } 3711251881Speter 3712251881Speter tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton)); 3713251881Speter tb->hb = hb; 3714251881Speter tb->fb = fb; 3715251881Speter tb->eb = eb; 3716251881Speter target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool); 3717251881Speter 3718251881Speter /* Prepare to apply the delta. */ 3719251881Speter svn_txdelta_apply(source, target, 3720251881Speter hb->new_text_base_md5_digest, 3721251881Speter hb->new_text_base_tmp_abspath /* error_info */, 3722251881Speter handler_pool, 3723251881Speter &hb->apply_handler, &hb->apply_baton); 3724251881Speter 3725251881Speter hb->pool = handler_pool; 3726251881Speter hb->fb = fb; 3727251881Speter 3728251881Speter /* We're all set. */ 3729251881Speter *handler_baton = hb; 3730251881Speter *handler = window_handler; 3731251881Speter 3732251881Speter return SVN_NO_ERROR; 3733251881Speter} 3734251881Speter 3735251881Speter 3736251881Speter/* An svn_delta_editor_t function. */ 3737251881Speterstatic svn_error_t * 3738251881Speterchange_file_prop(void *file_baton, 3739251881Speter const char *name, 3740251881Speter const svn_string_t *value, 3741251881Speter apr_pool_t *scratch_pool) 3742251881Speter{ 3743251881Speter struct file_baton *fb = file_baton; 3744251881Speter svn_prop_t *propchange; 3745251881Speter 3746251881Speter if (fb->skip_this) 3747251881Speter return SVN_NO_ERROR; 3748251881Speter 3749251881Speter /* Push a new propchange to the file baton's array of propchanges */ 3750251881Speter propchange = apr_array_push(fb->propchanges); 3751251881Speter propchange->name = apr_pstrdup(fb->pool, name); 3752251881Speter propchange->value = value ? svn_string_dup(value, fb->pool) : NULL; 3753251881Speter 3754251881Speter if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind) 3755251881Speter SVN_ERR(mark_file_edited(fb, scratch_pool)); 3756251881Speter 3757251881Speter if (! fb->shadowed 3758251881Speter && strcmp(name, SVN_PROP_SPECIAL) == 0) 3759251881Speter { 3760251881Speter struct edit_baton *eb = fb->edit_baton; 3761251881Speter svn_boolean_t modified = FALSE; 3762251881Speter svn_boolean_t becomes_symlink; 3763251881Speter svn_boolean_t was_symlink; 3764251881Speter 3765251881Speter /* Let's see if we have a change as in some scenarios servers report 3766251881Speter non-changes of properties. */ 3767251881Speter becomes_symlink = (value != NULL); 3768251881Speter 3769251881Speter if (fb->adding_file) 3770251881Speter was_symlink = becomes_symlink; /* No change */ 3771251881Speter else 3772251881Speter { 3773251881Speter apr_hash_t *props; 3774251881Speter 3775251881Speter /* We read the server-props, not the ACTUAL props here as we just 3776251881Speter want to see if this is really an incoming prop change. */ 3777251881Speter SVN_ERR(svn_wc__db_base_get_props(&props, eb->db, 3778251881Speter fb->local_abspath, 3779251881Speter scratch_pool, scratch_pool)); 3780251881Speter 3781251881Speter was_symlink = ((props 3782251881Speter && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL) 3783251881Speter ? svn_tristate_true 3784251881Speter : svn_tristate_false); 3785251881Speter } 3786251881Speter 3787251881Speter if (was_symlink != becomes_symlink) 3788251881Speter { 3789251881Speter /* If the local node was not modified, we continue as usual, if 3790251881Speter modified we want a tree conflict just like how we would handle 3791251881Speter it when receiving a delete + add (aka "replace") */ 3792251881Speter if (fb->local_prop_mods) 3793251881Speter modified = TRUE; 3794251881Speter else 3795251881Speter SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db, 3796251881Speter fb->local_abspath, 3797251881Speter FALSE, scratch_pool)); 3798251881Speter } 3799251881Speter 3800251881Speter if (modified) 3801251881Speter { 3802251881Speter if (!fb->edit_conflict) 3803251881Speter fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool); 3804251881Speter 3805251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3806251881Speter fb->edit_conflict, 3807251881Speter eb->db, fb->local_abspath, 3808251881Speter svn_wc_conflict_reason_edited, 3809251881Speter svn_wc_conflict_action_replace, 3810251881Speter NULL, 3811251881Speter fb->pool, scratch_pool)); 3812251881Speter 3813251881Speter SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, 3814251881Speter fb->local_abspath, fb->old_repos_relpath, 3815251881Speter fb->old_revision, fb->new_relpath, 3816251881Speter svn_node_file, svn_node_file, 3817251881Speter fb->pool, scratch_pool)); 3818251881Speter 3819251881Speter /* Create a copy of the existing (pre update) BASE node in WORKING, 3820251881Speter mark a tree conflict and handle the rest of the update as 3821251881Speter shadowed */ 3822251881Speter SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath, 3823251881Speter fb->edit_conflict, NULL, 3824251881Speter scratch_pool)); 3825251881Speter 3826251881Speter do_notification(eb, fb->local_abspath, svn_node_file, 3827251881Speter svn_wc_notify_tree_conflict, scratch_pool); 3828251881Speter 3829251881Speter /* Ok, we introduced a replacement, so we can now handle the rest 3830251881Speter as a normal shadowed update */ 3831251881Speter fb->shadowed = TRUE; 3832251881Speter fb->add_existed = FALSE; 3833251881Speter fb->already_notified = TRUE; 3834251881Speter } 3835251881Speter } 3836251881Speter 3837251881Speter return SVN_NO_ERROR; 3838251881Speter} 3839251881Speter 3840251881Speter/* Perform the actual merge of file changes between an original file, 3841251881Speter identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file 3842251881Speter identified by NEW_CHECKSUM. 3843251881Speter 3844251881Speter Merge the result into LOCAL_ABSPATH, which is part of the working copy 3845251881Speter identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming 3846251881Speter the intermediate files. 3847251881Speter 3848251881Speter The rest of the arguments are passed to svn_wc__internal_merge(). 3849251881Speter */ 3850251881Spetersvn_error_t * 3851251881Spetersvn_wc__perform_file_merge(svn_skel_t **work_items, 3852251881Speter svn_skel_t **conflict_skel, 3853251881Speter svn_boolean_t *found_conflict, 3854251881Speter svn_wc__db_t *db, 3855251881Speter const char *local_abspath, 3856251881Speter const char *wri_abspath, 3857251881Speter const svn_checksum_t *new_checksum, 3858251881Speter const svn_checksum_t *original_checksum, 3859251881Speter apr_hash_t *old_actual_props, 3860251881Speter const apr_array_header_t *ext_patterns, 3861251881Speter svn_revnum_t old_revision, 3862251881Speter svn_revnum_t target_revision, 3863251881Speter const apr_array_header_t *propchanges, 3864251881Speter const char *diff3_cmd, 3865251881Speter svn_cancel_func_t cancel_func, 3866251881Speter void *cancel_baton, 3867251881Speter apr_pool_t *result_pool, 3868251881Speter apr_pool_t *scratch_pool) 3869251881Speter{ 3870251881Speter /* Actual file exists and has local mods: 3871251881Speter Now we need to let loose svn_wc__internal_merge() to merge 3872251881Speter the textual changes into the working file. */ 3873251881Speter const char *oldrev_str, *newrev_str, *mine_str; 3874251881Speter const char *merge_left; 3875251881Speter svn_boolean_t delete_left = FALSE; 3876251881Speter const char *path_ext = ""; 3877251881Speter const char *new_text_base_tmp_abspath; 3878251881Speter enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged; 3879251881Speter svn_skel_t *work_item; 3880251881Speter 3881251881Speter *work_items = NULL; 3882251881Speter 3883251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath, 3884251881Speter db, wri_abspath, new_checksum, 3885251881Speter scratch_pool, scratch_pool)); 3886251881Speter 3887251881Speter /* If we have any file extensions we're supposed to 3888251881Speter preserve in generated conflict file names, then find 3889251881Speter this path's extension. But then, if it isn't one of 3890251881Speter the ones we want to keep in conflict filenames, 3891251881Speter pretend it doesn't have an extension at all. */ 3892251881Speter if (ext_patterns && ext_patterns->nelts) 3893251881Speter { 3894251881Speter svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool); 3895251881Speter if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns))) 3896251881Speter path_ext = ""; 3897251881Speter } 3898251881Speter 3899251881Speter /* old_revision can be invalid when the conflict is against a 3900251881Speter local addition */ 3901251881Speter if (!SVN_IS_VALID_REVNUM(old_revision)) 3902251881Speter old_revision = 0; 3903251881Speter 3904251881Speter oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s", 3905251881Speter old_revision, 3906251881Speter *path_ext ? "." : "", 3907251881Speter *path_ext ? path_ext : ""); 3908251881Speter 3909251881Speter newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s", 3910251881Speter target_revision, 3911251881Speter *path_ext ? "." : "", 3912251881Speter *path_ext ? path_ext : ""); 3913251881Speter mine_str = apr_psprintf(scratch_pool, ".mine%s%s", 3914251881Speter *path_ext ? "." : "", 3915251881Speter *path_ext ? path_ext : ""); 3916251881Speter 3917251881Speter if (! original_checksum) 3918251881Speter { 3919251881Speter SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath, 3920251881Speter result_pool, scratch_pool)); 3921251881Speter delete_left = TRUE; 3922251881Speter } 3923251881Speter else 3924251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath, 3925251881Speter original_checksum, 3926251881Speter result_pool, scratch_pool)); 3927251881Speter 3928251881Speter /* Merge the changes from the old textbase to the new 3929251881Speter textbase into the file we're updating. 3930251881Speter Remember that this function wants full paths! */ 3931251881Speter SVN_ERR(svn_wc__internal_merge(&work_item, 3932251881Speter conflict_skel, 3933251881Speter &merge_outcome, 3934251881Speter db, 3935251881Speter merge_left, 3936251881Speter new_text_base_tmp_abspath, 3937251881Speter local_abspath, 3938251881Speter wri_abspath, 3939251881Speter oldrev_str, newrev_str, mine_str, 3940251881Speter old_actual_props, 3941251881Speter FALSE /* dry_run */, 3942251881Speter diff3_cmd, NULL, propchanges, 3943251881Speter cancel_func, cancel_baton, 3944251881Speter result_pool, scratch_pool)); 3945251881Speter 3946251881Speter *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 3947251881Speter *found_conflict = (merge_outcome == svn_wc_merge_conflict); 3948251881Speter 3949251881Speter /* If we created a temporary left merge file, get rid of it. */ 3950251881Speter if (delete_left) 3951251881Speter { 3952251881Speter SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath, 3953251881Speter merge_left, 3954251881Speter result_pool, scratch_pool)); 3955251881Speter *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 3956251881Speter } 3957251881Speter 3958251881Speter return SVN_NO_ERROR; 3959251881Speter} 3960251881Speter 3961251881Speter/* This is the small planet. It has the complex responsibility of 3962251881Speter * "integrating" a new revision of a file into a working copy. 3963251881Speter * 3964251881Speter * Given a file_baton FB for a file either already under version control, or 3965251881Speter * prepared (see below) to join version control, fully install a 3966251881Speter * new revision of the file. 3967251881Speter * 3968251881Speter * ### transitional: installation of the working file will be handled 3969251881Speter * ### by the *INSTALL_PRISTINE flag. 3970251881Speter * 3971251881Speter * By "install", we mean: create a new text-base and prop-base, merge 3972251881Speter * any textual and property changes into the working file, and finally 3973251881Speter * update all metadata so that the working copy believes it has a new 3974251881Speter * working revision of the file. All of this work includes being 3975251881Speter * sensitive to eol translation, keyword substitution, and performing 3976251881Speter * all actions accumulated the parent directory's work queue. 3977251881Speter * 3978251881Speter * Set *CONTENT_STATE to the state of the contents after the 3979251881Speter * installation. 3980251881Speter * 3981251881Speter * Return values are allocated in RESULT_POOL and temporary allocations 3982251881Speter * are performed in SCRATCH_POOL. 3983251881Speter */ 3984251881Speterstatic svn_error_t * 3985251881Spetermerge_file(svn_skel_t **work_items, 3986251881Speter svn_skel_t **conflict_skel, 3987251881Speter svn_boolean_t *install_pristine, 3988251881Speter const char **install_from, 3989251881Speter svn_wc_notify_state_t *content_state, 3990251881Speter struct file_baton *fb, 3991251881Speter apr_hash_t *actual_props, 3992251881Speter apr_time_t last_changed_date, 3993251881Speter apr_pool_t *result_pool, 3994251881Speter apr_pool_t *scratch_pool) 3995251881Speter{ 3996251881Speter struct edit_baton *eb = fb->edit_baton; 3997251881Speter struct dir_baton *pb = fb->dir_baton; 3998251881Speter svn_boolean_t is_locally_modified; 3999251881Speter svn_boolean_t found_text_conflict = FALSE; 4000251881Speter 4001251881Speter SVN_ERR_ASSERT(! fb->shadowed 4002251881Speter && ! fb->obstruction_found 4003251881Speter && ! fb->edit_obstructed); 4004251881Speter 4005251881Speter /* 4006251881Speter When this function is called on file F, we assume the following 4007251881Speter things are true: 4008251881Speter 4009251881Speter - The new pristine text of F is present in the pristine store 4010251881Speter iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL. 4011251881Speter 4012251881Speter - The WC metadata still reflects the old version of F. 4013251881Speter (We can still access the old pristine base text of F.) 4014251881Speter 4015251881Speter The goal is to update the local working copy of F to reflect 4016251881Speter the changes received from the repository, preserving any local 4017251881Speter modifications. 4018251881Speter */ 4019251881Speter 4020251881Speter *work_items = NULL; 4021251881Speter *install_pristine = FALSE; 4022251881Speter *install_from = NULL; 4023251881Speter 4024251881Speter /* Start by splitting the file path, getting an access baton for the parent, 4025251881Speter and an entry for the file if any. */ 4026251881Speter 4027251881Speter /* Has the user made local mods to the working file? 4028251881Speter Note that this compares to the current pristine file, which is 4029251881Speter different from fb->old_text_base_path if we have a replaced-with-history 4030251881Speter file. However, in the case we had an obstruction, we check against the 4031251881Speter new text base. 4032251881Speter */ 4033251881Speter if (fb->adding_file && !fb->add_existed) 4034251881Speter { 4035251881Speter is_locally_modified = FALSE; /* There is no file: Don't check */ 4036251881Speter } 4037251881Speter else 4038251881Speter { 4039251881Speter /* The working file is not an obstruction. 4040251881Speter So: is the file modified, relative to its ORIGINAL pristine? 4041251881Speter 4042251881Speter This function sets is_locally_modified to FALSE for 4043251881Speter files that do not exist and for directories. */ 4044251881Speter 4045251881Speter SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified, 4046251881Speter eb->db, fb->local_abspath, 4047251881Speter FALSE /* exact_comparison */, 4048251881Speter scratch_pool)); 4049251881Speter } 4050251881Speter 4051251881Speter /* For 'textual' merging, we use the following system: 4052251881Speter 4053251881Speter When a file is modified and we have a new BASE: 4054251881Speter - For text files 4055251881Speter * svn_wc_merge uses diff3 4056251881Speter * possibly makes backups and marks files as conflicted. 4057251881Speter 4058251881Speter - For binary files 4059251881Speter * svn_wc_merge makes backups and marks files as conflicted. 4060251881Speter 4061251881Speter If a file is not modified and we have a new BASE: 4062251881Speter * Install from pristine. 4063251881Speter 4064251881Speter If we have property changes related to magic properties or if the 4065251881Speter svn:keywords property is set: 4066251881Speter * Retranslate from the working file. 4067251881Speter */ 4068251881Speter if (! is_locally_modified 4069251881Speter && fb->new_text_base_sha1_checksum) 4070251881Speter { 4071251881Speter /* If there are no local mods, who cares whether it's a text 4072251881Speter or binary file! Just write a log command to overwrite 4073251881Speter any working file with the new text-base. If newline 4074251881Speter conversion or keyword substitution is activated, this 4075251881Speter will happen as well during the copy. 4076251881Speter For replaced files, though, we want to merge in the changes 4077251881Speter even if the file is not modified compared to the (non-revert) 4078251881Speter text-base. */ 4079251881Speter 4080251881Speter *install_pristine = TRUE; 4081251881Speter } 4082251881Speter else if (fb->new_text_base_sha1_checksum) 4083251881Speter { 4084251881Speter /* Actual file exists and has local mods: 4085251881Speter Now we need to let loose svn_wc__merge_internal() to merge 4086251881Speter the textual changes into the working file. */ 4087251881Speter SVN_ERR(svn_wc__perform_file_merge(work_items, 4088251881Speter conflict_skel, 4089251881Speter &found_text_conflict, 4090251881Speter eb->db, 4091251881Speter fb->local_abspath, 4092251881Speter pb->local_abspath, 4093251881Speter fb->new_text_base_sha1_checksum, 4094251881Speter fb->add_existed 4095251881Speter ? NULL 4096251881Speter : fb->original_checksum, 4097251881Speter actual_props, 4098251881Speter eb->ext_patterns, 4099251881Speter fb->old_revision, 4100251881Speter *eb->target_revision, 4101251881Speter fb->propchanges, 4102251881Speter eb->diff3_cmd, 4103251881Speter eb->cancel_func, eb->cancel_baton, 4104251881Speter result_pool, scratch_pool)); 4105251881Speter } /* end: working file exists and has mods */ 4106251881Speter else 4107251881Speter { 4108251881Speter /* There is no new text base, but let's see if the working file needs 4109251881Speter to be updated for any other reason. */ 4110251881Speter 4111251881Speter apr_hash_t *keywords; 4112251881Speter 4113251881Speter /* Determine if any of the propchanges are the "magic" ones that 4114251881Speter might require changing the working file. */ 4115251881Speter svn_boolean_t magic_props_changed; 4116251881Speter 4117251881Speter magic_props_changed = svn_wc__has_magic_property(fb->propchanges); 4118251881Speter 4119251881Speter SVN_ERR(svn_wc__get_translate_info(NULL, NULL, 4120251881Speter &keywords, 4121251881Speter NULL, 4122251881Speter eb->db, fb->local_abspath, 4123251881Speter actual_props, TRUE, 4124251881Speter scratch_pool, scratch_pool)); 4125251881Speter if (magic_props_changed || keywords) 4126251881Speter { 4127251881Speter /* Special edge-case: it's possible that this file installation 4128251881Speter only involves propchanges, but that some of those props still 4129251881Speter require a retranslation of the working file. 4130251881Speter 4131251881Speter OR that the file doesn't involve propchanges which by themselves 4132251881Speter require retranslation, but receiving a change bumps the revision 4133251881Speter number which requires re-expansion of keywords... */ 4134251881Speter 4135251881Speter if (is_locally_modified) 4136251881Speter { 4137251881Speter const char *tmptext; 4138251881Speter 4139251881Speter /* Copy and DEtranslate the working file to a temp text-base. 4140251881Speter Note that detranslation is done according to the old props. */ 4141251881Speter SVN_ERR(svn_wc__internal_translated_file( 4142251881Speter &tmptext, fb->local_abspath, eb->db, fb->local_abspath, 4143251881Speter SVN_WC_TRANSLATE_TO_NF 4144251881Speter | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP, 4145251881Speter eb->cancel_func, eb->cancel_baton, 4146251881Speter result_pool, scratch_pool)); 4147251881Speter 4148251881Speter /* We always want to reinstall the working file if the magic 4149251881Speter properties have changed, or there are any keywords present. 4150251881Speter Note that TMPTEXT might actually refer to the working file 4151251881Speter itself (the above function skips a detranslate when not 4152251881Speter required). This is acceptable, as we will (re)translate 4153251881Speter according to the new properties into a temporary file (from 4154251881Speter the working file), and then rename the temp into place. Magic! 4155251881Speter */ 4156251881Speter *install_pristine = TRUE; 4157251881Speter *install_from = tmptext; 4158251881Speter } 4159251881Speter else 4160251881Speter { 4161251881Speter /* Use our existing 'copy' from the pristine store instead 4162251881Speter of making a new copy. This way we can use the standard code 4163251881Speter to update the recorded size and modification time. 4164251881Speter (Issue #3842) */ 4165251881Speter *install_pristine = TRUE; 4166251881Speter } 4167251881Speter } 4168251881Speter } 4169251881Speter 4170251881Speter /* Set the returned content state. */ 4171251881Speter 4172251881Speter if (found_text_conflict) 4173251881Speter *content_state = svn_wc_notify_state_conflicted; 4174251881Speter else if (fb->new_text_base_sha1_checksum) 4175251881Speter { 4176251881Speter if (is_locally_modified) 4177251881Speter *content_state = svn_wc_notify_state_merged; 4178251881Speter else 4179251881Speter *content_state = svn_wc_notify_state_changed; 4180251881Speter } 4181251881Speter else 4182251881Speter *content_state = svn_wc_notify_state_unchanged; 4183251881Speter 4184251881Speter return SVN_NO_ERROR; 4185251881Speter} 4186251881Speter 4187251881Speter 4188251881Speter/* An svn_delta_editor_t function. */ 4189251881Speter/* Mostly a wrapper around merge_file. */ 4190251881Speterstatic svn_error_t * 4191251881Speterclose_file(void *file_baton, 4192251881Speter const char *expected_md5_digest, 4193251881Speter apr_pool_t *pool) 4194251881Speter{ 4195251881Speter struct file_baton *fb = file_baton; 4196251881Speter struct dir_baton *pdb = fb->dir_baton; 4197251881Speter struct edit_baton *eb = fb->edit_baton; 4198251881Speter svn_wc_notify_state_t content_state, prop_state; 4199251881Speter svn_wc_notify_lock_state_t lock_state; 4200251881Speter svn_checksum_t *expected_md5_checksum = NULL; 4201251881Speter apr_hash_t *new_base_props = NULL; 4202251881Speter apr_hash_t *new_actual_props = NULL; 4203251881Speter apr_array_header_t *entry_prop_changes; 4204251881Speter apr_array_header_t *dav_prop_changes; 4205251881Speter apr_array_header_t *regular_prop_changes; 4206251881Speter apr_hash_t *current_base_props = NULL; 4207251881Speter apr_hash_t *current_actual_props = NULL; 4208251881Speter apr_hash_t *local_actual_props = NULL; 4209251881Speter svn_skel_t *all_work_items = NULL; 4210251881Speter svn_skel_t *conflict_skel = NULL; 4211251881Speter svn_skel_t *work_item; 4212251881Speter apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */ 4213251881Speter svn_boolean_t keep_recorded_info = FALSE; 4214251881Speter const svn_checksum_t *new_checksum; 4215251881Speter apr_array_header_t *iprops = NULL; 4216251881Speter 4217251881Speter if (fb->skip_this) 4218251881Speter { 4219251881Speter svn_pool_destroy(fb->pool); 4220251881Speter SVN_ERR(maybe_release_dir_info(pdb)); 4221251881Speter return SVN_NO_ERROR; 4222251881Speter } 4223251881Speter 4224251881Speter if (fb->edited) 4225251881Speter conflict_skel = fb->edit_conflict; 4226251881Speter 4227251881Speter if (expected_md5_digest) 4228251881Speter SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5, 4229251881Speter expected_md5_digest, scratch_pool)); 4230251881Speter 4231251881Speter if (fb->new_text_base_md5_checksum && expected_md5_checksum 4232251881Speter && !svn_checksum_match(expected_md5_checksum, 4233251881Speter fb->new_text_base_md5_checksum)) 4234251881Speter return svn_error_trace( 4235251881Speter svn_checksum_mismatch_err(expected_md5_checksum, 4236251881Speter fb->new_text_base_md5_checksum, 4237251881Speter scratch_pool, 4238251881Speter _("Checksum mismatch for '%s'"), 4239251881Speter svn_dirent_local_style( 4240251881Speter fb->local_abspath, pool))); 4241251881Speter 4242251881Speter /* Gather the changes for each kind of property. */ 4243251881Speter SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes, 4244251881Speter &dav_prop_changes, ®ular_prop_changes, 4245251881Speter scratch_pool)); 4246251881Speter 4247251881Speter /* Extract the changed_* and lock state information. */ 4248251881Speter { 4249251881Speter svn_revnum_t new_changed_rev; 4250251881Speter apr_time_t new_changed_date; 4251251881Speter const char *new_changed_author; 4252251881Speter 4253251881Speter SVN_ERR(accumulate_last_change(&new_changed_rev, 4254251881Speter &new_changed_date, 4255251881Speter &new_changed_author, 4256251881Speter entry_prop_changes, 4257251881Speter scratch_pool, scratch_pool)); 4258251881Speter 4259251881Speter if (SVN_IS_VALID_REVNUM(new_changed_rev)) 4260251881Speter fb->changed_rev = new_changed_rev; 4261251881Speter if (new_changed_date != 0) 4262251881Speter fb->changed_date = new_changed_date; 4263251881Speter if (new_changed_author != NULL) 4264251881Speter fb->changed_author = new_changed_author; 4265251881Speter } 4266251881Speter 4267251881Speter /* Determine whether the file has become unlocked. */ 4268251881Speter { 4269251881Speter int i; 4270251881Speter 4271251881Speter lock_state = svn_wc_notify_lock_state_unchanged; 4272251881Speter 4273251881Speter for (i = 0; i < entry_prop_changes->nelts; ++i) 4274251881Speter { 4275251881Speter const svn_prop_t *prop 4276251881Speter = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t); 4277251881Speter 4278251881Speter /* If we see a change to the LOCK_TOKEN entry prop, then the only 4279251881Speter possible change is its REMOVAL. Thus, the lock has been removed, 4280251881Speter and we should likewise remove our cached copy of it. */ 4281251881Speter if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN)) 4282251881Speter { 4283251881Speter /* If we lose the lock, but not because we are switching to 4284251881Speter another url, remove the state lock from the wc */ 4285251881Speter if (! eb->switch_relpath 4286251881Speter || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0) 4287251881Speter { 4288251881Speter SVN_ERR_ASSERT(prop->value == NULL); 4289251881Speter SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, 4290251881Speter scratch_pool)); 4291251881Speter 4292251881Speter lock_state = svn_wc_notify_lock_state_unlocked; 4293251881Speter } 4294251881Speter break; 4295251881Speter } 4296251881Speter } 4297251881Speter } 4298251881Speter 4299251881Speter /* Install all kinds of properties. It is important to do this before 4300251881Speter any file content merging, since that process might expand keywords, in 4301251881Speter which case we want the new entryprops to be in place. */ 4302251881Speter 4303251881Speter /* Write log commands to merge REGULAR_PROPS into the existing 4304251881Speter properties of FB->LOCAL_ABSPATH. Update *PROP_STATE to reflect 4305251881Speter the result of the regular prop merge. 4306251881Speter 4307251881Speter BASE_PROPS and WORKING_PROPS are hashes of the base and 4308251881Speter working props of the file; if NULL they are read from the wc. */ 4309251881Speter 4310251881Speter /* ### some of this feels like voodoo... */ 4311251881Speter 4312251881Speter if ((!fb->adding_file || fb->add_existed) 4313251881Speter && !fb->shadowed) 4314251881Speter SVN_ERR(svn_wc__get_actual_props(&local_actual_props, 4315251881Speter eb->db, fb->local_abspath, 4316251881Speter scratch_pool, scratch_pool)); 4317251881Speter if (local_actual_props == NULL) 4318251881Speter local_actual_props = apr_hash_make(scratch_pool); 4319251881Speter 4320251881Speter if (fb->add_existed) 4321251881Speter { 4322251881Speter /* This node already exists. Grab the current pristine properties. */ 4323251881Speter SVN_ERR(svn_wc__db_read_pristine_props(¤t_base_props, 4324251881Speter eb->db, fb->local_abspath, 4325251881Speter scratch_pool, scratch_pool)); 4326251881Speter current_actual_props = local_actual_props; 4327251881Speter } 4328251881Speter else if (!fb->adding_file) 4329251881Speter { 4330251881Speter /* Get the BASE properties for proper merging. */ 4331251881Speter SVN_ERR(svn_wc__db_base_get_props(¤t_base_props, 4332251881Speter eb->db, fb->local_abspath, 4333251881Speter scratch_pool, scratch_pool)); 4334251881Speter current_actual_props = local_actual_props; 4335251881Speter } 4336251881Speter 4337251881Speter /* Note: even if the node existed before, it may not have 4338251881Speter pristine props (e.g a local-add) */ 4339251881Speter if (current_base_props == NULL) 4340251881Speter current_base_props = apr_hash_make(scratch_pool); 4341251881Speter 4342251881Speter /* And new nodes need an empty set of ACTUAL props. */ 4343251881Speter if (current_actual_props == NULL) 4344251881Speter current_actual_props = apr_hash_make(scratch_pool); 4345251881Speter 4346251881Speter prop_state = svn_wc_notify_state_unknown; 4347251881Speter 4348251881Speter if (! fb->shadowed) 4349251881Speter { 4350251881Speter svn_boolean_t install_pristine; 4351251881Speter const char *install_from = NULL; 4352251881Speter 4353251881Speter /* Merge the 'regular' props into the existing working proplist. */ 4354251881Speter /* This will merge the old and new props into a new prop db, and 4355251881Speter write <cp> commands to the logfile to install the merged 4356251881Speter props. */ 4357251881Speter new_base_props = svn_prop__patch(current_base_props, regular_prop_changes, 4358251881Speter scratch_pool); 4359251881Speter SVN_ERR(svn_wc__merge_props(&conflict_skel, 4360251881Speter &prop_state, 4361251881Speter &new_actual_props, 4362251881Speter eb->db, 4363251881Speter fb->local_abspath, 4364251881Speter NULL /* server_baseprops (update, not merge) */, 4365251881Speter current_base_props, 4366251881Speter current_actual_props, 4367251881Speter regular_prop_changes, /* propchanges */ 4368251881Speter scratch_pool, 4369251881Speter scratch_pool)); 4370251881Speter /* We will ALWAYS have properties to save (after a not-dry-run merge). */ 4371251881Speter SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL); 4372251881Speter 4373251881Speter /* Merge the text. This will queue some additional work. */ 4374251881Speter if (!fb->obstruction_found && !fb->edit_obstructed) 4375251881Speter { 4376251881Speter svn_error_t *err; 4377251881Speter err = merge_file(&work_item, &conflict_skel, 4378251881Speter &install_pristine, &install_from, 4379251881Speter &content_state, fb, current_actual_props, 4380251881Speter fb->changed_date, scratch_pool, scratch_pool); 4381251881Speter 4382251881Speter if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED) 4383251881Speter { 4384251881Speter if (eb->notify_func) 4385251881Speter { 4386251881Speter svn_wc_notify_t *notify =svn_wc_create_notify( 4387251881Speter fb->local_abspath, 4388251881Speter svn_wc_notify_update_skip_access_denied, 4389251881Speter scratch_pool); 4390251881Speter 4391251881Speter notify->kind = svn_node_file; 4392251881Speter notify->err = err; 4393251881Speter 4394251881Speter eb->notify_func(eb->notify_baton, notify, scratch_pool); 4395251881Speter } 4396251881Speter svn_error_clear(err); 4397251881Speter 4398251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, 4399251881Speter scratch_pool)); 4400251881Speter fb->skip_this = TRUE; 4401251881Speter 4402251881Speter svn_pool_destroy(fb->pool); 4403251881Speter SVN_ERR(maybe_release_dir_info(pdb)); 4404251881Speter return SVN_NO_ERROR; 4405251881Speter } 4406251881Speter else 4407251881Speter SVN_ERR(err); 4408251881Speter 4409251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4410251881Speter scratch_pool); 4411251881Speter } 4412251881Speter else 4413251881Speter { 4414251881Speter install_pristine = FALSE; 4415251881Speter if (fb->new_text_base_sha1_checksum) 4416251881Speter content_state = svn_wc_notify_state_changed; 4417251881Speter else 4418251881Speter content_state = svn_wc_notify_state_unchanged; 4419251881Speter } 4420251881Speter 4421251881Speter if (install_pristine) 4422251881Speter { 4423251881Speter svn_boolean_t record_fileinfo; 4424251881Speter 4425251881Speter /* If we are installing from the pristine contents, then go ahead and 4426251881Speter record the fileinfo. That will be the "proper" values. Installing 4427251881Speter from some random file means the fileinfo does NOT correspond to 4428251881Speter the pristine (in which case, the fileinfo will be cleared for 4429251881Speter safety's sake). */ 4430251881Speter record_fileinfo = (install_from == NULL); 4431251881Speter 4432251881Speter SVN_ERR(svn_wc__wq_build_file_install(&work_item, 4433251881Speter eb->db, 4434251881Speter fb->local_abspath, 4435251881Speter install_from, 4436251881Speter eb->use_commit_times, 4437251881Speter record_fileinfo, 4438251881Speter scratch_pool, scratch_pool)); 4439251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4440251881Speter scratch_pool); 4441251881Speter } 4442251881Speter else if (lock_state == svn_wc_notify_lock_state_unlocked 4443251881Speter && !fb->obstruction_found) 4444251881Speter { 4445251881Speter /* If a lock was removed and we didn't update the text contents, we 4446251881Speter might need to set the file read-only. 4447251881Speter 4448251881Speter Note: this will also update the executable flag, but ... meh. */ 4449251881Speter SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db, 4450251881Speter fb->local_abspath, 4451251881Speter scratch_pool, scratch_pool)); 4452251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4453251881Speter scratch_pool); 4454251881Speter } 4455251881Speter 4456251881Speter if (! install_pristine 4457251881Speter && (content_state == svn_wc_notify_state_unchanged)) 4458251881Speter { 4459251881Speter /* It is safe to keep the current recorded timestamp and size */ 4460251881Speter keep_recorded_info = TRUE; 4461251881Speter } 4462251881Speter 4463251881Speter /* Clean up any temporary files. */ 4464251881Speter 4465251881Speter /* Remove the INSTALL_FROM file, as long as it doesn't refer to the 4466251881Speter working file. */ 4467251881Speter if (install_from != NULL 4468251881Speter && strcmp(install_from, fb->local_abspath) != 0) 4469251881Speter { 4470251881Speter SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db, 4471251881Speter fb->local_abspath, install_from, 4472251881Speter scratch_pool, scratch_pool)); 4473251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4474251881Speter scratch_pool); 4475251881Speter } 4476251881Speter } 4477251881Speter else 4478251881Speter { 4479251881Speter /* Adding or updating a BASE node under a locally added node. */ 4480251881Speter apr_hash_t *fake_actual_props; 4481251881Speter 4482251881Speter if (fb->adding_file) 4483251881Speter fake_actual_props = apr_hash_make(scratch_pool); 4484251881Speter else 4485251881Speter fake_actual_props = current_base_props; 4486251881Speter 4487251881Speter /* Store the incoming props (sent as propchanges) in new_base_props 4488251881Speter and create a set of new actual props to use for notifications */ 4489251881Speter new_base_props = svn_prop__patch(current_base_props, regular_prop_changes, 4490251881Speter scratch_pool); 4491251881Speter SVN_ERR(svn_wc__merge_props(&conflict_skel, 4492251881Speter &prop_state, 4493251881Speter &new_actual_props, 4494251881Speter eb->db, 4495251881Speter fb->local_abspath, 4496251881Speter NULL /* server_baseprops (not merging) */, 4497251881Speter current_base_props /* pristine_props */, 4498251881Speter fake_actual_props /* actual_props */, 4499251881Speter regular_prop_changes, /* propchanges */ 4500251881Speter scratch_pool, 4501251881Speter scratch_pool)); 4502251881Speter 4503251881Speter if (fb->new_text_base_sha1_checksum) 4504251881Speter content_state = svn_wc_notify_state_changed; 4505251881Speter else 4506251881Speter content_state = svn_wc_notify_state_unchanged; 4507251881Speter } 4508251881Speter 4509251881Speter /* Insert/replace the BASE node with all of the new metadata. */ 4510251881Speter 4511251881Speter /* Set the 'checksum' column of the file's BASE_NODE row to 4512251881Speter * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that 4513251881Speter * checksum is already in the pristine store. */ 4514251881Speter new_checksum = fb->new_text_base_sha1_checksum; 4515251881Speter 4516251881Speter /* If we don't have a NEW checksum, then the base must not have changed. 4517251881Speter Just carry over the old checksum. */ 4518251881Speter if (new_checksum == NULL) 4519251881Speter new_checksum = fb->original_checksum; 4520251881Speter 4521251881Speter if (conflict_skel) 4522251881Speter { 4523251881Speter SVN_ERR(complete_conflict(conflict_skel, 4524251881Speter fb->edit_baton, 4525251881Speter fb->local_abspath, 4526251881Speter fb->old_repos_relpath, 4527251881Speter fb->old_revision, 4528251881Speter fb->new_relpath, 4529251881Speter svn_node_file, svn_node_file, 4530251881Speter fb->pool, scratch_pool)); 4531251881Speter 4532251881Speter SVN_ERR(svn_wc__conflict_create_markers(&work_item, 4533251881Speter eb->db, fb->local_abspath, 4534251881Speter conflict_skel, 4535251881Speter scratch_pool, scratch_pool)); 4536251881Speter 4537251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4538251881Speter scratch_pool); 4539251881Speter } 4540251881Speter 4541251881Speter /* Any inherited props to be set set for this base node? */ 4542251881Speter if (eb->wcroot_iprops) 4543251881Speter { 4544251881Speter iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath); 4545251881Speter 4546251881Speter /* close_edit may also update iprops for switched nodes, catching 4547251881Speter those for which close_directory is never called (e.g. a switch 4548251881Speter with no changes). So as a minor optimization we remove any 4549251881Speter iprops from the hash so as not to set them again in 4550251881Speter close_edit. */ 4551251881Speter if (iprops) 4552251881Speter svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL); 4553251881Speter } 4554251881Speter 4555251881Speter SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath, 4556251881Speter eb->wcroot_abspath, 4557251881Speter fb->new_relpath, 4558251881Speter eb->repos_root, eb->repos_uuid, 4559251881Speter *eb->target_revision, 4560251881Speter new_base_props, 4561251881Speter fb->changed_rev, 4562251881Speter fb->changed_date, 4563251881Speter fb->changed_author, 4564251881Speter new_checksum, 4565251881Speter (dav_prop_changes->nelts > 0) 4566251881Speter ? svn_prop_array_to_hash( 4567251881Speter dav_prop_changes, 4568251881Speter scratch_pool) 4569251881Speter : NULL, 4570251881Speter (fb->add_existed && fb->adding_file), 4571251881Speter (! fb->shadowed) && new_base_props, 4572251881Speter new_actual_props, 4573251881Speter iprops, 4574251881Speter keep_recorded_info, 4575251881Speter (fb->shadowed && fb->obstruction_found), 4576251881Speter conflict_skel, 4577251881Speter all_work_items, 4578251881Speter scratch_pool)); 4579251881Speter 4580251881Speter if (conflict_skel && eb->conflict_func) 4581251881Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, 4582251881Speter conflict_skel, 4583251881Speter NULL /* merge_options */, 4584251881Speter eb->conflict_func, 4585251881Speter eb->conflict_baton, 4586251881Speter eb->cancel_func, 4587251881Speter eb->cancel_baton, 4588251881Speter scratch_pool)); 4589251881Speter 4590251881Speter /* Deal with the WORKING tree, based on updates to the BASE tree. */ 4591251881Speter 4592251881Speter svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL); 4593251881Speter 4594251881Speter /* Send a notification to the callback function. (Skip notifications 4595251881Speter about files which were already notified for another reason.) */ 4596251881Speter if (eb->notify_func && !fb->already_notified 4597251881Speter && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked)) 4598251881Speter { 4599251881Speter svn_wc_notify_t *notify; 4600251881Speter svn_wc_notify_action_t action = svn_wc_notify_update_update; 4601251881Speter 4602251881Speter if (fb->edited) 4603251881Speter { 4604251881Speter if (fb->shadowed || fb->edit_obstructed) 4605251881Speter action = fb->adding_file 4606251881Speter ? svn_wc_notify_update_shadowed_add 4607251881Speter : svn_wc_notify_update_shadowed_update; 4608251881Speter else if (fb->obstruction_found || fb->add_existed) 4609251881Speter { 4610251881Speter if (content_state != svn_wc_notify_state_conflicted) 4611251881Speter action = svn_wc_notify_exists; 4612251881Speter } 4613251881Speter else if (fb->adding_file) 4614251881Speter { 4615251881Speter action = svn_wc_notify_update_add; 4616251881Speter } 4617251881Speter } 4618251881Speter else 4619251881Speter { 4620251881Speter SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked); 4621251881Speter action = svn_wc_notify_update_broken_lock; 4622251881Speter } 4623251881Speter 4624251881Speter /* If the file was moved-away, notify for the moved-away node. 4625251881Speter * The original location only had its BASE info changed and 4626251881Speter * we don't usually notify about such changes. */ 4627251881Speter notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool); 4628251881Speter notify->kind = svn_node_file; 4629251881Speter notify->content_state = content_state; 4630251881Speter notify->prop_state = prop_state; 4631251881Speter notify->lock_state = lock_state; 4632251881Speter notify->revision = *eb->target_revision; 4633251881Speter notify->old_revision = fb->old_revision; 4634251881Speter 4635251881Speter /* Fetch the mimetype from the actual properties */ 4636251881Speter notify->mime_type = svn_prop_get_value(new_actual_props, 4637251881Speter SVN_PROP_MIME_TYPE); 4638251881Speter 4639251881Speter eb->notify_func(eb->notify_baton, notify, scratch_pool); 4640251881Speter } 4641251881Speter 4642251881Speter svn_pool_destroy(fb->pool); /* Destroy scratch_pool */ 4643251881Speter 4644251881Speter /* We have one less referrer to the directory */ 4645251881Speter SVN_ERR(maybe_release_dir_info(pdb)); 4646251881Speter 4647251881Speter return SVN_NO_ERROR; 4648251881Speter} 4649251881Speter 4650251881Speter 4651251881Speter/* An svn_delta_editor_t function. */ 4652251881Speterstatic svn_error_t * 4653251881Speterclose_edit(void *edit_baton, 4654251881Speter apr_pool_t *pool) 4655251881Speter{ 4656251881Speter struct edit_baton *eb = edit_baton; 4657251881Speter apr_pool_t *scratch_pool = eb->pool; 4658251881Speter 4659251881Speter /* The editor didn't even open the root; we have to take care of 4660251881Speter some cleanup stuffs. */ 4661251881Speter if (! eb->root_opened 4662251881Speter && *eb->target_basename == '\0') 4663251881Speter { 4664251881Speter /* We need to "un-incomplete" the root directory. */ 4665251881Speter SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db, 4666251881Speter eb->anchor_abspath, 4667251881Speter scratch_pool)); 4668251881Speter } 4669251881Speter 4670251881Speter /* By definition, anybody "driving" this editor for update or switch 4671251881Speter purposes at a *minimum* must have called set_target_revision() at 4672251881Speter the outset, and close_edit() at the end -- even if it turned out 4673251881Speter that no changes ever had to be made, and open_root() was never 4674251881Speter called. That's fine. But regardless, when the edit is over, 4675251881Speter this editor needs to make sure that *all* paths have had their 4676251881Speter revisions bumped to the new target revision. */ 4677251881Speter 4678251881Speter /* Make sure our update target now has the new working revision. 4679251881Speter Also, if this was an 'svn switch', then rewrite the target's 4680251881Speter url. All of this tweaking might happen recursively! Note 4681251881Speter that if eb->target is NULL, that's okay (albeit "sneaky", 4682251881Speter some might say). */ 4683251881Speter 4684251881Speter /* Extra check: if the update did nothing but make its target 4685251881Speter 'deleted', then do *not* run cleanup on the target, as it 4686251881Speter will only remove the deleted entry! */ 4687251881Speter if (! eb->target_deleted) 4688251881Speter { 4689251881Speter SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db, 4690251881Speter eb->target_abspath, 4691251881Speter eb->requested_depth, 4692251881Speter eb->switch_relpath, 4693251881Speter eb->repos_root, 4694251881Speter eb->repos_uuid, 4695251881Speter *(eb->target_revision), 4696251881Speter eb->skipped_trees, 4697251881Speter eb->wcroot_iprops, 4698251881Speter eb->notify_func, 4699251881Speter eb->notify_baton, 4700251881Speter eb->pool)); 4701251881Speter 4702251881Speter if (*eb->target_basename != '\0') 4703251881Speter { 4704251881Speter svn_wc__db_status_t status; 4705251881Speter svn_error_t *err; 4706251881Speter 4707251881Speter /* Note: we are fetching information about the *target*, not anchor. 4708251881Speter There is no guarantee that the target has a BASE node. 4709251881Speter For example: 4710251881Speter 4711251881Speter The node was not present in BASE, but locally-added, and the 4712251881Speter update did not create a new BASE node "under" the local-add. 4713251881Speter 4714251881Speter If there is no BASE node for the target, then we certainly don't 4715251881Speter have to worry about removing it. */ 4716251881Speter err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL, 4717251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 4718251881Speter NULL, NULL, NULL, NULL, 4719251881Speter eb->db, eb->target_abspath, 4720251881Speter scratch_pool, scratch_pool); 4721251881Speter if (err) 4722251881Speter { 4723251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 4724251881Speter return svn_error_trace(err); 4725251881Speter 4726251881Speter svn_error_clear(err); 4727251881Speter } 4728251881Speter else if (status == svn_wc__db_status_excluded) 4729251881Speter { 4730251881Speter /* There is a small chance that the explicit target of an update/ 4731251881Speter switch is gone in the repository, in that specific case the 4732251881Speter node hasn't been re-added to the BASE tree by this update. 4733251881Speter 4734251881Speter If so, we should get rid of this excluded node now. */ 4735251881Speter 4736251881Speter SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath, 4737251881Speter FALSE /* keep_as_working */, 4738251881Speter FALSE /* queue_deletes */, 4739253734Speter FALSE /* remove_locks */, 4740251881Speter SVN_INVALID_REVNUM, 4741251881Speter NULL, NULL, scratch_pool)); 4742251881Speter } 4743251881Speter } 4744251881Speter } 4745251881Speter 4746251881Speter /* The edit is over: run the wq with proper cancel support, 4747251881Speter but first kill the handler that would run it on the pool 4748251881Speter cleanup at the end of this function. */ 4749251881Speter apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton); 4750251881Speter 4751251881Speter SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath, 4752251881Speter eb->cancel_func, eb->cancel_baton, 4753251881Speter eb->pool)); 4754251881Speter 4755251881Speter /* The edit is over, free its pool. 4756251881Speter ### No, this is wrong. Who says this editor/baton won't be used 4757251881Speter again? But the change is not merely to remove this call. We 4758251881Speter should also make eb->pool not be a subpool (see make_editor), 4759251881Speter and change callers of svn_client_{checkout,update,switch} to do 4760251881Speter better pool management. ### */ 4761251881Speter 4762251881Speter svn_pool_destroy(eb->pool); 4763251881Speter 4764251881Speter return SVN_NO_ERROR; 4765251881Speter} 4766251881Speter 4767251881Speter 4768251881Speter/*** Returning editors. ***/ 4769251881Speter 4770251881Speter/* Helper for the three public editor-supplying functions. */ 4771251881Speterstatic svn_error_t * 4772251881Spetermake_editor(svn_revnum_t *target_revision, 4773251881Speter svn_wc__db_t *db, 4774251881Speter const char *anchor_abspath, 4775251881Speter const char *target_basename, 4776251881Speter apr_hash_t *wcroot_iprops, 4777251881Speter svn_boolean_t use_commit_times, 4778251881Speter const char *switch_url, 4779251881Speter svn_depth_t depth, 4780251881Speter svn_boolean_t depth_is_sticky, 4781251881Speter svn_boolean_t allow_unver_obstructions, 4782251881Speter svn_boolean_t adds_as_modification, 4783251881Speter svn_boolean_t server_performs_filtering, 4784251881Speter svn_boolean_t clean_checkout, 4785251881Speter svn_wc_notify_func2_t notify_func, 4786251881Speter void *notify_baton, 4787251881Speter svn_cancel_func_t cancel_func, 4788251881Speter void *cancel_baton, 4789251881Speter svn_wc_dirents_func_t fetch_dirents_func, 4790251881Speter void *fetch_dirents_baton, 4791251881Speter svn_wc_conflict_resolver_func2_t conflict_func, 4792251881Speter void *conflict_baton, 4793251881Speter svn_wc_external_update_t external_func, 4794251881Speter void *external_baton, 4795251881Speter const char *diff3_cmd, 4796251881Speter const apr_array_header_t *preserved_exts, 4797251881Speter const svn_delta_editor_t **editor, 4798251881Speter void **edit_baton, 4799251881Speter apr_pool_t *result_pool, 4800251881Speter apr_pool_t *scratch_pool) 4801251881Speter{ 4802251881Speter struct edit_baton *eb; 4803251881Speter void *inner_baton; 4804251881Speter apr_pool_t *edit_pool = svn_pool_create(result_pool); 4805251881Speter svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool); 4806251881Speter const svn_delta_editor_t *inner_editor; 4807251881Speter const char *repos_root, *repos_uuid; 4808251881Speter struct svn_wc__shim_fetch_baton_t *sfb; 4809251881Speter svn_delta_shim_callbacks_t *shim_callbacks = 4810251881Speter svn_delta_shim_callbacks_default(edit_pool); 4811251881Speter 4812251881Speter /* An unknown depth can't be sticky. */ 4813251881Speter if (depth == svn_depth_unknown) 4814251881Speter depth_is_sticky = FALSE; 4815251881Speter 4816251881Speter /* Get the anchor's repository root and uuid. The anchor must already exist 4817251881Speter in BASE. */ 4818251881Speter SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid, 4819251881Speter db, anchor_abspath, 4820251881Speter result_pool, scratch_pool)); 4821251881Speter 4822251881Speter /* With WC-NG we need a valid repository root */ 4823251881Speter SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL); 4824251881Speter 4825251881Speter /* Disallow a switch operation to change the repository root of the target, 4826251881Speter if that is known. */ 4827251881Speter if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url)) 4828251881Speter return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL, 4829251881Speter _("'%s'\nis not the same repository as\n'%s'"), 4830251881Speter switch_url, repos_root); 4831251881Speter 4832251881Speter /* Construct an edit baton. */ 4833251881Speter eb = apr_pcalloc(edit_pool, sizeof(*eb)); 4834251881Speter eb->pool = edit_pool; 4835251881Speter eb->use_commit_times = use_commit_times; 4836251881Speter eb->target_revision = target_revision; 4837251881Speter eb->repos_root = repos_root; 4838251881Speter eb->repos_uuid = repos_uuid; 4839251881Speter eb->db = db; 4840251881Speter eb->target_basename = target_basename; 4841251881Speter eb->anchor_abspath = anchor_abspath; 4842251881Speter eb->wcroot_iprops = wcroot_iprops; 4843251881Speter 4844251881Speter SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath, 4845251881Speter edit_pool, scratch_pool)); 4846251881Speter 4847251881Speter if (switch_url) 4848251881Speter eb->switch_relpath = 4849251881Speter svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool); 4850251881Speter else 4851251881Speter eb->switch_relpath = NULL; 4852251881Speter 4853251881Speter if (svn_path_is_empty(target_basename)) 4854251881Speter eb->target_abspath = eb->anchor_abspath; 4855251881Speter else 4856251881Speter eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename, 4857251881Speter edit_pool); 4858251881Speter 4859251881Speter eb->requested_depth = depth; 4860251881Speter eb->depth_is_sticky = depth_is_sticky; 4861251881Speter eb->notify_func = notify_func; 4862251881Speter eb->notify_baton = notify_baton; 4863251881Speter eb->external_func = external_func; 4864251881Speter eb->external_baton = external_baton; 4865251881Speter eb->diff3_cmd = diff3_cmd; 4866251881Speter eb->cancel_func = cancel_func; 4867251881Speter eb->cancel_baton = cancel_baton; 4868251881Speter eb->conflict_func = conflict_func; 4869251881Speter eb->conflict_baton = conflict_baton; 4870251881Speter eb->allow_unver_obstructions = allow_unver_obstructions; 4871251881Speter eb->adds_as_modification = adds_as_modification; 4872251881Speter eb->clean_checkout = clean_checkout; 4873251881Speter eb->skipped_trees = apr_hash_make(edit_pool); 4874251881Speter eb->dir_dirents = apr_hash_make(edit_pool); 4875251881Speter eb->ext_patterns = preserved_exts; 4876251881Speter 4877251881Speter apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton, 4878251881Speter apr_pool_cleanup_null); 4879251881Speter 4880251881Speter /* Construct an editor. */ 4881251881Speter tree_editor->set_target_revision = set_target_revision; 4882251881Speter tree_editor->open_root = open_root; 4883251881Speter tree_editor->delete_entry = delete_entry; 4884251881Speter tree_editor->add_directory = add_directory; 4885251881Speter tree_editor->open_directory = open_directory; 4886251881Speter tree_editor->change_dir_prop = change_dir_prop; 4887251881Speter tree_editor->close_directory = close_directory; 4888251881Speter tree_editor->absent_directory = absent_directory; 4889251881Speter tree_editor->add_file = add_file; 4890251881Speter tree_editor->open_file = open_file; 4891251881Speter tree_editor->apply_textdelta = apply_textdelta; 4892251881Speter tree_editor->change_file_prop = change_file_prop; 4893251881Speter tree_editor->close_file = close_file; 4894251881Speter tree_editor->absent_file = absent_file; 4895251881Speter tree_editor->close_edit = close_edit; 4896251881Speter 4897251881Speter /* Fiddle with the type system. */ 4898251881Speter inner_editor = tree_editor; 4899251881Speter inner_baton = eb; 4900251881Speter 4901251881Speter if (!depth_is_sticky 4902251881Speter && depth != svn_depth_unknown 4903251881Speter && svn_depth_empty <= depth && depth < svn_depth_infinity 4904251881Speter && fetch_dirents_func) 4905251881Speter { 4906251881Speter /* We are asked to perform an update at a depth less than the ambient 4907251881Speter depth. In this case the update won't describe additions that would 4908251881Speter have been reported if we updated at the ambient depth. */ 4909251881Speter svn_error_t *err; 4910251881Speter svn_node_kind_t dir_kind; 4911251881Speter svn_wc__db_status_t dir_status; 4912251881Speter const char *dir_repos_relpath; 4913251881Speter svn_depth_t dir_depth; 4914251881Speter 4915251881Speter /* we have to do this on the target of the update, not the anchor */ 4916251881Speter err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL, 4917251881Speter &dir_repos_relpath, NULL, NULL, NULL, 4918251881Speter NULL, NULL, &dir_depth, NULL, NULL, NULL, 4919251881Speter NULL, NULL, NULL, 4920251881Speter db, eb->target_abspath, 4921251881Speter scratch_pool, scratch_pool); 4922251881Speter 4923251881Speter if (!err 4924251881Speter && dir_kind == svn_node_dir 4925251881Speter && dir_status == svn_wc__db_status_normal) 4926251881Speter { 4927251881Speter if (dir_depth > depth) 4928251881Speter { 4929251881Speter apr_hash_t *dirents; 4930251881Speter 4931251881Speter /* If we switch, we should look at the new relpath */ 4932251881Speter if (eb->switch_relpath) 4933251881Speter dir_repos_relpath = eb->switch_relpath; 4934251881Speter 4935251881Speter SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, 4936251881Speter repos_root, dir_repos_relpath, 4937251881Speter edit_pool, scratch_pool)); 4938251881Speter 4939251881Speter if (dirents != NULL && apr_hash_count(dirents)) 4940251881Speter svn_hash_sets(eb->dir_dirents, 4941251881Speter apr_pstrdup(edit_pool, dir_repos_relpath), 4942251881Speter dirents); 4943251881Speter } 4944251881Speter 4945251881Speter if (depth == svn_depth_immediates) 4946251881Speter { 4947251881Speter /* Worst case scenario of issue #3569 fix: We have to do the 4948251881Speter same for all existing subdirs, but then we check for 4949251881Speter svn_depth_empty. */ 4950251881Speter const apr_array_header_t *children; 4951251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 4952251881Speter int i; 4953251881Speter SVN_ERR(svn_wc__db_base_get_children(&children, db, 4954251881Speter eb->target_abspath, 4955251881Speter scratch_pool, 4956251881Speter iterpool)); 4957251881Speter 4958251881Speter for (i = 0; i < children->nelts; i++) 4959251881Speter { 4960251881Speter const char *child_abspath; 4961251881Speter const char *child_name; 4962251881Speter 4963251881Speter svn_pool_clear(iterpool); 4964251881Speter 4965251881Speter child_name = APR_ARRAY_IDX(children, i, const char *); 4966251881Speter 4967251881Speter child_abspath = svn_dirent_join(eb->target_abspath, 4968251881Speter child_name, iterpool); 4969251881Speter 4970251881Speter SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind, 4971251881Speter NULL, &dir_repos_relpath, 4972251881Speter NULL, NULL, NULL, NULL, 4973251881Speter NULL, &dir_depth, NULL, 4974251881Speter NULL, NULL, NULL, NULL, 4975251881Speter NULL, 4976251881Speter db, child_abspath, 4977251881Speter iterpool, iterpool)); 4978251881Speter 4979251881Speter if (dir_kind == svn_node_dir 4980251881Speter && dir_status == svn_wc__db_status_normal 4981251881Speter && dir_depth > svn_depth_empty) 4982251881Speter { 4983251881Speter apr_hash_t *dirents; 4984251881Speter 4985251881Speter /* If we switch, we should look at the new relpath */ 4986251881Speter if (eb->switch_relpath) 4987251881Speter dir_repos_relpath = svn_relpath_join( 4988251881Speter eb->switch_relpath, 4989251881Speter child_name, iterpool); 4990251881Speter 4991251881Speter SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, 4992251881Speter repos_root, dir_repos_relpath, 4993251881Speter edit_pool, iterpool)); 4994251881Speter 4995251881Speter if (dirents != NULL && apr_hash_count(dirents)) 4996251881Speter svn_hash_sets(eb->dir_dirents, 4997251881Speter apr_pstrdup(edit_pool, 4998251881Speter dir_repos_relpath), 4999251881Speter dirents); 5000251881Speter } 5001251881Speter } 5002251881Speter } 5003251881Speter } 5004251881Speter else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 5005251881Speter svn_error_clear(err); 5006251881Speter else 5007251881Speter SVN_ERR(err); 5008251881Speter } 5009251881Speter 5010251881Speter /* We need to limit the scope of our operation to the ambient depths 5011251881Speter present in the working copy already, but only if the requested 5012251881Speter depth is not sticky. If a depth was explicitly requested, 5013251881Speter libsvn_delta/depth_filter_editor.c will ensure that we never see 5014251881Speter editor calls that extend beyond the scope of the requested depth. 5015251881Speter But even what we do so might extend beyond the scope of our 5016251881Speter ambient depth. So we use another filtering editor to avoid 5017251881Speter modifying the ambient working copy depth when not asked to do so. 5018251881Speter (This can also be skipped if the server understands depth.) */ 5019251881Speter if (!server_performs_filtering 5020251881Speter && !depth_is_sticky) 5021251881Speter SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor, 5022251881Speter &inner_baton, 5023251881Speter db, 5024251881Speter anchor_abspath, 5025251881Speter target_basename, 5026251881Speter inner_editor, 5027251881Speter inner_baton, 5028251881Speter result_pool)); 5029251881Speter 5030251881Speter SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, 5031251881Speter cancel_baton, 5032251881Speter inner_editor, 5033251881Speter inner_baton, 5034251881Speter editor, 5035251881Speter edit_baton, 5036251881Speter result_pool)); 5037251881Speter 5038251881Speter sfb = apr_palloc(result_pool, sizeof(*sfb)); 5039251881Speter sfb->db = db; 5040251881Speter sfb->base_abspath = eb->anchor_abspath; 5041251881Speter sfb->fetch_base = TRUE; 5042251881Speter 5043251881Speter shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func; 5044251881Speter shim_callbacks->fetch_props_func = svn_wc__fetch_props_func; 5045251881Speter shim_callbacks->fetch_base_func = svn_wc__fetch_base_func; 5046251881Speter shim_callbacks->fetch_baton = sfb; 5047251881Speter 5048251881Speter SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, 5049251881Speter NULL, NULL, shim_callbacks, 5050251881Speter result_pool, scratch_pool)); 5051251881Speter 5052251881Speter return SVN_NO_ERROR; 5053251881Speter} 5054251881Speter 5055251881Speter 5056251881Spetersvn_error_t * 5057251881Spetersvn_wc__get_update_editor(const svn_delta_editor_t **editor, 5058251881Speter void **edit_baton, 5059251881Speter svn_revnum_t *target_revision, 5060251881Speter svn_wc_context_t *wc_ctx, 5061251881Speter const char *anchor_abspath, 5062251881Speter const char *target_basename, 5063251881Speter apr_hash_t *wcroot_iprops, 5064251881Speter svn_boolean_t use_commit_times, 5065251881Speter svn_depth_t depth, 5066251881Speter svn_boolean_t depth_is_sticky, 5067251881Speter svn_boolean_t allow_unver_obstructions, 5068251881Speter svn_boolean_t adds_as_modification, 5069251881Speter svn_boolean_t server_performs_filtering, 5070251881Speter svn_boolean_t clean_checkout, 5071251881Speter const char *diff3_cmd, 5072251881Speter const apr_array_header_t *preserved_exts, 5073251881Speter svn_wc_dirents_func_t fetch_dirents_func, 5074251881Speter void *fetch_dirents_baton, 5075251881Speter svn_wc_conflict_resolver_func2_t conflict_func, 5076251881Speter void *conflict_baton, 5077251881Speter svn_wc_external_update_t external_func, 5078251881Speter void *external_baton, 5079251881Speter svn_cancel_func_t cancel_func, 5080251881Speter void *cancel_baton, 5081251881Speter svn_wc_notify_func2_t notify_func, 5082251881Speter void *notify_baton, 5083251881Speter apr_pool_t *result_pool, 5084251881Speter apr_pool_t *scratch_pool) 5085251881Speter{ 5086251881Speter return make_editor(target_revision, wc_ctx->db, anchor_abspath, 5087251881Speter target_basename, wcroot_iprops, use_commit_times, 5088251881Speter NULL, depth, depth_is_sticky, allow_unver_obstructions, 5089251881Speter adds_as_modification, server_performs_filtering, 5090251881Speter clean_checkout, 5091251881Speter notify_func, notify_baton, 5092251881Speter cancel_func, cancel_baton, 5093251881Speter fetch_dirents_func, fetch_dirents_baton, 5094251881Speter conflict_func, conflict_baton, 5095251881Speter external_func, external_baton, 5096251881Speter diff3_cmd, preserved_exts, editor, edit_baton, 5097251881Speter result_pool, scratch_pool); 5098251881Speter} 5099251881Speter 5100251881Spetersvn_error_t * 5101251881Spetersvn_wc__get_switch_editor(const svn_delta_editor_t **editor, 5102251881Speter void **edit_baton, 5103251881Speter svn_revnum_t *target_revision, 5104251881Speter svn_wc_context_t *wc_ctx, 5105251881Speter const char *anchor_abspath, 5106251881Speter const char *target_basename, 5107251881Speter const char *switch_url, 5108251881Speter apr_hash_t *wcroot_iprops, 5109251881Speter svn_boolean_t use_commit_times, 5110251881Speter svn_depth_t depth, 5111251881Speter svn_boolean_t depth_is_sticky, 5112251881Speter svn_boolean_t allow_unver_obstructions, 5113251881Speter svn_boolean_t server_performs_filtering, 5114251881Speter const char *diff3_cmd, 5115251881Speter const apr_array_header_t *preserved_exts, 5116251881Speter svn_wc_dirents_func_t fetch_dirents_func, 5117251881Speter void *fetch_dirents_baton, 5118251881Speter svn_wc_conflict_resolver_func2_t conflict_func, 5119251881Speter void *conflict_baton, 5120251881Speter svn_wc_external_update_t external_func, 5121251881Speter void *external_baton, 5122251881Speter svn_cancel_func_t cancel_func, 5123251881Speter void *cancel_baton, 5124251881Speter svn_wc_notify_func2_t notify_func, 5125251881Speter void *notify_baton, 5126251881Speter apr_pool_t *result_pool, 5127251881Speter apr_pool_t *scratch_pool) 5128251881Speter{ 5129251881Speter SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool)); 5130251881Speter 5131251881Speter return make_editor(target_revision, wc_ctx->db, anchor_abspath, 5132251881Speter target_basename, wcroot_iprops, use_commit_times, 5133251881Speter switch_url, 5134251881Speter depth, depth_is_sticky, allow_unver_obstructions, 5135251881Speter FALSE /* adds_as_modification */, 5136251881Speter server_performs_filtering, 5137251881Speter FALSE /* clean_checkout */, 5138251881Speter notify_func, notify_baton, 5139251881Speter cancel_func, cancel_baton, 5140251881Speter fetch_dirents_func, fetch_dirents_baton, 5141251881Speter conflict_func, conflict_baton, 5142251881Speter external_func, external_baton, 5143251881Speter diff3_cmd, preserved_exts, 5144251881Speter editor, edit_baton, 5145251881Speter result_pool, scratch_pool); 5146251881Speter} 5147251881Speter 5148251881Speter 5149251881Speter 5150251881Speter/* ### Note that this function is completely different from the rest of the 5151251881Speter update editor in what it updates. The update editor changes only BASE 5152251881Speter and ACTUAL and this function just changes WORKING and ACTUAL. 5153251881Speter 5154251881Speter In the entries world this function shared a lot of code with the 5155251881Speter update editor but in the wonderful new WC-NG world it will probably 5156251881Speter do more and more by itself and would be more logically grouped with 5157251881Speter the add/copy functionality in adm_ops.c and copy.c. */ 5158251881Spetersvn_error_t * 5159251881Spetersvn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, 5160251881Speter const char *local_abspath, 5161251881Speter svn_stream_t *new_base_contents, 5162251881Speter svn_stream_t *new_contents, 5163251881Speter apr_hash_t *new_base_props, 5164251881Speter apr_hash_t *new_props, 5165251881Speter const char *copyfrom_url, 5166251881Speter svn_revnum_t copyfrom_rev, 5167251881Speter svn_cancel_func_t cancel_func, 5168251881Speter void *cancel_baton, 5169251881Speter apr_pool_t *scratch_pool) 5170251881Speter{ 5171251881Speter svn_wc__db_t *db = wc_ctx->db; 5172251881Speter const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 5173251881Speter svn_wc__db_status_t status; 5174251881Speter svn_node_kind_t kind; 5175251881Speter const char *tmp_text_base_abspath; 5176251881Speter svn_checksum_t *new_text_base_md5_checksum; 5177251881Speter svn_checksum_t *new_text_base_sha1_checksum; 5178251881Speter const char *source_abspath = NULL; 5179251881Speter svn_skel_t *all_work_items = NULL; 5180251881Speter svn_skel_t *work_item; 5181251881Speter const char *repos_root_url; 5182251881Speter const char *repos_uuid; 5183251881Speter const char *original_repos_relpath; 5184251881Speter svn_revnum_t changed_rev; 5185251881Speter apr_time_t changed_date; 5186251881Speter const char *changed_author; 5187251881Speter svn_error_t *err; 5188251881Speter apr_pool_t *pool = scratch_pool; 5189251881Speter 5190251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 5191251881Speter SVN_ERR_ASSERT(new_base_contents != NULL); 5192251881Speter SVN_ERR_ASSERT(new_base_props != NULL); 5193251881Speter 5194251881Speter /* We should have a write lock on this file's parent directory. */ 5195251881Speter SVN_ERR(svn_wc__write_check(db, dir_abspath, pool)); 5196251881Speter 5197251881Speter err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5198251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5199251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5200251881Speter NULL, NULL, NULL, 5201251881Speter db, local_abspath, scratch_pool, scratch_pool); 5202251881Speter 5203251881Speter if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 5204251881Speter return svn_error_trace(err); 5205251881Speter else if(err) 5206251881Speter svn_error_clear(err); 5207251881Speter else 5208251881Speter switch (status) 5209251881Speter { 5210251881Speter case svn_wc__db_status_not_present: 5211251881Speter case svn_wc__db_status_deleted: 5212251881Speter break; 5213251881Speter default: 5214251881Speter return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, 5215251881Speter _("Node '%s' exists."), 5216251881Speter svn_dirent_local_style(local_abspath, 5217251881Speter scratch_pool)); 5218251881Speter } 5219251881Speter 5220251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url, 5221251881Speter &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, 5222251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5223251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5224251881Speter db, dir_abspath, scratch_pool, scratch_pool)); 5225251881Speter 5226251881Speter switch (status) 5227251881Speter { 5228251881Speter case svn_wc__db_status_normal: 5229251881Speter case svn_wc__db_status_added: 5230251881Speter break; 5231251881Speter case svn_wc__db_status_deleted: 5232251881Speter return 5233251881Speter svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL, 5234251881Speter _("Can't add '%s' to a parent directory" 5235251881Speter " scheduled for deletion"), 5236251881Speter svn_dirent_local_style(local_abspath, 5237251881Speter scratch_pool)); 5238251881Speter default: 5239251881Speter return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err, 5240251881Speter _("Can't find parent directory's node while" 5241251881Speter " trying to add '%s'"), 5242251881Speter svn_dirent_local_style(local_abspath, 5243251881Speter scratch_pool)); 5244251881Speter } 5245251881Speter if (kind != svn_node_dir) 5246251881Speter return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 5247251881Speter _("Can't schedule an addition of '%s'" 5248251881Speter " below a not-directory node"), 5249251881Speter svn_dirent_local_style(local_abspath, 5250251881Speter scratch_pool)); 5251251881Speter 5252251881Speter /* Fabricate the anticipated new URL of the target and check the 5253251881Speter copyfrom URL to be in the same repository. */ 5254251881Speter if (copyfrom_url != NULL) 5255251881Speter { 5256251881Speter /* Find the repository_root via the parent directory, which 5257251881Speter is always versioned before this function is called */ 5258251881Speter 5259251881Speter if (!repos_root_url) 5260251881Speter { 5261251881Speter /* The parent is an addition, scan upwards to find the right info */ 5262251881Speter SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, 5263251881Speter &repos_root_url, &repos_uuid, 5264251881Speter NULL, NULL, NULL, NULL, 5265251881Speter wc_ctx->db, dir_abspath, 5266251881Speter scratch_pool, scratch_pool)); 5267251881Speter } 5268251881Speter SVN_ERR_ASSERT(repos_root_url); 5269251881Speter 5270251881Speter original_repos_relpath = 5271251881Speter svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool); 5272251881Speter 5273251881Speter if (!original_repos_relpath) 5274251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 5275251881Speter _("Copyfrom-url '%s' has different repository" 5276251881Speter " root than '%s'"), 5277251881Speter copyfrom_url, repos_root_url); 5278251881Speter } 5279251881Speter else 5280251881Speter { 5281251881Speter original_repos_relpath = NULL; 5282251881Speter copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */ 5283251881Speter } 5284251881Speter 5285251881Speter /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and 5286251881Speter filter NEW_BASE_PROPS so it contains only regular props. */ 5287251881Speter { 5288251881Speter apr_array_header_t *regular_props; 5289251881Speter apr_array_header_t *entry_props; 5290251881Speter 5291251881Speter SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool), 5292251881Speter &entry_props, NULL, ®ular_props, 5293251881Speter pool)); 5294251881Speter 5295251881Speter /* Put regular props back into a hash table. */ 5296251881Speter new_base_props = svn_prop_array_to_hash(regular_props, pool); 5297251881Speter 5298251881Speter /* Get the change_* info from the entry props. */ 5299251881Speter SVN_ERR(accumulate_last_change(&changed_rev, 5300251881Speter &changed_date, 5301251881Speter &changed_author, 5302251881Speter entry_props, pool, pool)); 5303251881Speter } 5304251881Speter 5305251881Speter /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to 5306251881Speter it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its 5307251881Speter NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */ 5308251881Speter { 5309251881Speter svn_stream_t *tmp_base_contents; 5310251881Speter 5311251881Speter SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents, 5312251881Speter &tmp_text_base_abspath, 5313251881Speter &new_text_base_md5_checksum, 5314251881Speter &new_text_base_sha1_checksum, 5315251881Speter wc_ctx->db, local_abspath, 5316251881Speter pool, pool)); 5317251881Speter SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents, 5318251881Speter cancel_func, cancel_baton, pool)); 5319251881Speter } 5320251881Speter 5321251881Speter /* If the caller gave us a new working file, copy it to a safe (temporary) 5322251881Speter location and set SOURCE_ABSPATH to that path. We'll then translate/copy 5323251881Speter that into place after the node's state has been created. */ 5324251881Speter if (new_contents) 5325251881Speter { 5326251881Speter const char *temp_dir_abspath; 5327251881Speter svn_stream_t *tmp_contents; 5328251881Speter 5329251881Speter SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, 5330251881Speter local_abspath, pool, pool)); 5331251881Speter SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath, 5332251881Speter temp_dir_abspath, svn_io_file_del_none, 5333251881Speter pool, pool)); 5334251881Speter SVN_ERR(svn_stream_copy3(new_contents, tmp_contents, 5335251881Speter cancel_func, cancel_baton, pool)); 5336251881Speter } 5337251881Speter 5338251881Speter /* Install new text base for copied files. Added files do NOT have a 5339251881Speter text base. */ 5340251881Speter if (copyfrom_url != NULL) 5341251881Speter { 5342251881Speter SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath, 5343251881Speter new_text_base_sha1_checksum, 5344251881Speter new_text_base_md5_checksum, pool)); 5345251881Speter } 5346251881Speter else 5347251881Speter { 5348251881Speter /* ### There's something wrong around here. Sometimes (merge from a 5349251881Speter foreign repository, at least) we are called with copyfrom_url = 5350251881Speter NULL and an empty new_base_contents (and an empty set of 5351251881Speter new_base_props). Why an empty "new base"? 5352251881Speter 5353251881Speter That happens in merge_tests.py 54,87,88,89,143. 5354251881Speter 5355251881Speter In that case, having been given this supposed "new base" file, we 5356251881Speter copy it and calculate its checksum but do not install it. Why? 5357251881Speter That must be wrong. 5358251881Speter 5359251881Speter To crudely work around one issue with this, that we shouldn't 5360251881Speter record a checksum in the database if we haven't installed the 5361251881Speter corresponding pristine text, for now we'll just set the checksum 5362251881Speter to NULL. 5363251881Speter 5364251881Speter The proper solution is probably more like: the caller should pass 5365251881Speter NULL for the missing information, and this function should learn to 5366251881Speter handle that. */ 5367251881Speter 5368251881Speter new_text_base_sha1_checksum = NULL; 5369251881Speter new_text_base_md5_checksum = NULL; 5370251881Speter } 5371251881Speter 5372251881Speter /* For added files without NEW_CONTENTS, then generate the working file 5373251881Speter from the provided "pristine" contents. */ 5374251881Speter if (new_contents == NULL && copyfrom_url == NULL) 5375251881Speter source_abspath = tmp_text_base_abspath; 5376251881Speter 5377251881Speter { 5378251881Speter svn_boolean_t record_fileinfo; 5379251881Speter 5380251881Speter /* If new contents were provided, then we do NOT want to record the 5381251881Speter file information. We assume the new contents do not match the 5382251881Speter "proper" values for RECORDED_SIZE and RECORDED_TIME. */ 5383251881Speter record_fileinfo = (new_contents == NULL); 5384251881Speter 5385251881Speter /* Install the working copy file (with appropriate translation) from 5386251881Speter the appropriate source. SOURCE_ABSPATH will be NULL, indicating an 5387251881Speter installation from the pristine (available for copied/moved files), 5388251881Speter or it will specify a temporary file where we placed a "pristine" 5389251881Speter (for an added file) or a detranslated local-mods file. */ 5390251881Speter SVN_ERR(svn_wc__wq_build_file_install(&work_item, 5391251881Speter db, local_abspath, 5392251881Speter source_abspath, 5393251881Speter FALSE /* use_commit_times */, 5394251881Speter record_fileinfo, 5395251881Speter pool, pool)); 5396251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); 5397251881Speter 5398251881Speter /* If we installed from somewhere besides the official pristine, then 5399251881Speter it is a temporary file, which needs to be removed. */ 5400251881Speter if (source_abspath != NULL) 5401251881Speter { 5402251881Speter SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath, 5403251881Speter source_abspath, 5404251881Speter pool, pool)); 5405251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); 5406251881Speter } 5407251881Speter } 5408251881Speter 5409251881Speter /* ### ideally, we would have a single DB operation, and queue the work 5410251881Speter ### items on that. for now, we'll queue them with the second call. */ 5411251881Speter 5412251881Speter SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath, 5413251881Speter new_base_props, 5414251881Speter changed_rev, 5415251881Speter changed_date, 5416251881Speter changed_author, 5417251881Speter original_repos_relpath, 5418251881Speter original_repos_relpath ? repos_root_url 5419251881Speter : NULL, 5420251881Speter original_repos_relpath ? repos_uuid : NULL, 5421251881Speter copyfrom_rev, 5422251881Speter new_text_base_sha1_checksum, 5423251881Speter TRUE, 5424251881Speter new_props, 5425251881Speter FALSE /* is_move */, 5426251881Speter NULL /* conflict */, 5427251881Speter all_work_items, 5428251881Speter pool)); 5429251881Speter 5430251881Speter return svn_error_trace(svn_wc__wq_run(db, dir_abspath, 5431251881Speter cancel_func, cancel_baton, 5432251881Speter pool)); 5433251881Speter} 5434251881Speter 5435251881Spetersvn_error_t * 5436251881Spetersvn_wc__complete_directory_add(svn_wc_context_t *wc_ctx, 5437251881Speter const char *local_abspath, 5438251881Speter apr_hash_t *new_original_props, 5439251881Speter const char *copyfrom_url, 5440251881Speter svn_revnum_t copyfrom_rev, 5441251881Speter apr_pool_t *scratch_pool) 5442251881Speter{ 5443251881Speter svn_wc__db_status_t status; 5444251881Speter svn_node_kind_t kind; 5445251881Speter const char *original_repos_relpath; 5446251881Speter const char *original_root_url; 5447251881Speter const char *original_uuid; 5448251881Speter svn_boolean_t had_props; 5449251881Speter svn_boolean_t props_mod; 5450251881Speter 5451251881Speter svn_revnum_t original_revision; 5452251881Speter svn_revnum_t changed_rev; 5453251881Speter apr_time_t changed_date; 5454251881Speter const char *changed_author; 5455251881Speter 5456251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 5457251881Speter NULL, NULL, NULL, NULL, NULL, 5458251881Speter &original_repos_relpath, &original_root_url, 5459251881Speter &original_uuid, &original_revision, NULL, NULL, 5460251881Speter NULL, NULL, NULL, NULL, &had_props, &props_mod, 5461251881Speter NULL, NULL, NULL, 5462251881Speter wc_ctx->db, local_abspath, 5463251881Speter scratch_pool, scratch_pool)); 5464251881Speter 5465251881Speter if (status != svn_wc__db_status_added 5466251881Speter || kind != svn_node_dir 5467251881Speter || had_props 5468251881Speter || props_mod 5469251881Speter || !original_repos_relpath) 5470251881Speter { 5471251881Speter return svn_error_createf( 5472251881Speter SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 5473251881Speter _("'%s' is not an unmodified copied directory"), 5474251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 5475251881Speter } 5476251881Speter if (original_revision != copyfrom_rev 5477251881Speter || strcmp(copyfrom_url, 5478251881Speter svn_path_url_add_component2(original_root_url, 5479251881Speter original_repos_relpath, 5480251881Speter scratch_pool))) 5481251881Speter { 5482251881Speter return svn_error_createf( 5483251881Speter SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL, 5484251881Speter _("Copyfrom '%s' doesn't match original location of '%s'"), 5485251881Speter copyfrom_url, 5486251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 5487251881Speter } 5488251881Speter 5489251881Speter { 5490251881Speter apr_array_header_t *regular_props; 5491251881Speter apr_array_header_t *entry_props; 5492251881Speter 5493251881Speter SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props, 5494251881Speter scratch_pool), 5495251881Speter &entry_props, NULL, ®ular_props, 5496251881Speter scratch_pool)); 5497251881Speter 5498251881Speter /* Put regular props back into a hash table. */ 5499251881Speter new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool); 5500251881Speter 5501251881Speter /* Get the change_* info from the entry props. */ 5502251881Speter SVN_ERR(accumulate_last_change(&changed_rev, 5503251881Speter &changed_date, 5504251881Speter &changed_author, 5505251881Speter entry_props, scratch_pool, scratch_pool)); 5506251881Speter } 5507251881Speter 5508251881Speter return svn_error_trace( 5509251881Speter svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath, 5510251881Speter new_original_props, 5511251881Speter changed_rev, changed_date, changed_author, 5512251881Speter original_repos_relpath, original_root_url, 5513251881Speter original_uuid, original_revision, 5514251881Speter NULL /* children */, 5515251881Speter FALSE /* is_move */, 5516251881Speter svn_depth_infinity, 5517251881Speter NULL /* conflict */, 5518251881Speter NULL /* work_items */, 5519251881Speter scratch_pool)); 5520251881Speter} 5521