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. */ 214289180Speter const char *switch_repos_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 261289180Speter /* After closing the root directory a copy of its edited value */ 262289180Speter svn_boolean_t edited; 263289180Speter 264251881Speter apr_pool_t *pool; 265251881Speter}; 266251881Speter 267251881Speter 268251881Speter/* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being 269251881Speter * updated. 270251881Speter * 271251881Speter * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string 272251881Speter * LOCAL_ABSPATH. 273251881Speter */ 274251881Speterstatic svn_error_t * 275251881Speterremember_skipped_tree(struct edit_baton *eb, 276251881Speter const char *local_abspath, 277251881Speter apr_pool_t *scratch_pool) 278251881Speter{ 279251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 280251881Speter 281251881Speter svn_hash_sets(eb->skipped_trees, 282251881Speter apr_pstrdup(eb->pool, 283251881Speter svn_dirent_skip_ancestor(eb->wcroot_abspath, 284251881Speter local_abspath)), 285251881Speter (void *)1); 286251881Speter 287251881Speter return SVN_NO_ERROR; 288251881Speter} 289251881Speter 290251881Speter/* Per directory baton. Lives in its own subpool of the parent directory 291251881Speter or of the edit baton if there is no parent directory */ 292251881Speterstruct dir_baton 293251881Speter{ 294251881Speter /* Basename of this directory. */ 295251881Speter const char *name; 296251881Speter 297251881Speter /* Absolute path of this directory */ 298251881Speter const char *local_abspath; 299251881Speter 300251881Speter /* The repository relative path this directory will correspond to. */ 301289180Speter const char *new_repos_relpath; 302251881Speter 303251881Speter /* The revision of the directory before updating */ 304251881Speter svn_revnum_t old_revision; 305251881Speter 306251881Speter /* The repos_relpath before updating/switching */ 307251881Speter const char *old_repos_relpath; 308251881Speter 309251881Speter /* The global edit baton. */ 310251881Speter struct edit_baton *edit_baton; 311251881Speter 312251881Speter /* Baton for this directory's parent, or NULL if this is the root 313251881Speter directory. */ 314251881Speter struct dir_baton *parent_baton; 315251881Speter 316251881Speter /* Set if updates to this directory are skipped */ 317251881Speter svn_boolean_t skip_this; 318251881Speter 319251881Speter /* Set if there was a previous notification for this directory */ 320251881Speter svn_boolean_t already_notified; 321251881Speter 322251881Speter /* Set if this directory is being added during this editor drive. */ 323251881Speter svn_boolean_t adding_dir; 324251881Speter 325251881Speter /* Set on a node and its descendants are not present in the working copy 326251881Speter but should still be updated (not skipped). These nodes should all be 327251881Speter marked as deleted. */ 328251881Speter svn_boolean_t shadowed; 329251881Speter 330251881Speter /* Set on a node when the existing node is obstructed, and the edit operation 331251881Speter continues as semi-shadowed update */ 332251881Speter svn_boolean_t edit_obstructed; 333251881Speter 334251881Speter /* The (new) changed_* information, cached to avoid retrieving it later */ 335251881Speter svn_revnum_t changed_rev; 336251881Speter apr_time_t changed_date; 337251881Speter const char *changed_author; 338251881Speter 339251881Speter /* If not NULL, contains a mapping of const char* basenames of children that 340251881Speter have been deleted to their svn_skel_t* tree conflicts. 341251881Speter We store this hash to allow replacements to continue under a just 342251881Speter installed tree conflict. 343251881Speter 344251881Speter The add after the delete will then update the tree conflicts information 345251881Speter and reinstall it. */ 346251881Speter apr_hash_t *deletion_conflicts; 347251881Speter 348289180Speter /* A hash of file names (only the hash key matters) seen by add_file and 349289180Speter add_directory and not yet added to the database, mapping to a const 350289180Speter char * node kind (via svn_node_kind_to_word(). */ 351289180Speter apr_hash_t *not_present_nodes; 352251881Speter 353251881Speter /* Set if an unversioned dir of the same name already existed in 354251881Speter this directory. */ 355251881Speter svn_boolean_t obstruction_found; 356251881Speter 357251881Speter /* Set if a dir of the same name already exists and is 358251881Speter scheduled for addition without history. */ 359251881Speter svn_boolean_t add_existed; 360251881Speter 361251881Speter /* An array of svn_prop_t structures, representing all the property 362251881Speter changes to be applied to this directory. */ 363251881Speter apr_array_header_t *propchanges; 364251881Speter 365251881Speter /* A boolean indicating whether this node or one of its children has 366251881Speter received any 'real' changes. Used to avoid tree conflicts for simple 367251881Speter entryprop changes, like lock management */ 368251881Speter svn_boolean_t edited; 369251881Speter 370251881Speter /* The tree conflict to install once the node is really edited */ 371251881Speter svn_skel_t *edit_conflict; 372251881Speter 373251881Speter /* The bump information for this directory. */ 374251881Speter struct bump_dir_info *bump_info; 375251881Speter 376251881Speter /* The depth of the directory in the wc (or inferred if added). Not 377251881Speter used for filtering; we have a separate wrapping editor for that. */ 378251881Speter svn_depth_t ambient_depth; 379251881Speter 380251881Speter /* Was the directory marked as incomplete before the update? 381251881Speter (In other words, are we resuming an interrupted update?) 382251881Speter 383251881Speter If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes 384251881Speter and properties for/of the directory. If WAS_INCOMPLETE is FALSE then 385251881Speter we only receive the changes in/for children and properties.*/ 386251881Speter svn_boolean_t was_incomplete; 387251881Speter 388251881Speter /* The pool in which this baton itself is allocated. */ 389251881Speter apr_pool_t *pool; 390251881Speter 391251881Speter /* how many nodes are referring to baton? */ 392251881Speter int ref_count; 393251881Speter 394251881Speter}; 395251881Speter 396251881Speter 397251881Speterstruct handler_baton 398251881Speter{ 399251881Speter svn_txdelta_window_handler_t apply_handler; 400251881Speter void *apply_baton; 401251881Speter apr_pool_t *pool; 402251881Speter struct file_baton *fb; 403251881Speter 404251881Speter /* Where we are assembling the new file. */ 405289180Speter svn_wc__db_install_data_t *install_data; 406251881Speter 407251881Speter /* The expected source checksum of the text source or NULL if no base 408251881Speter checksum is available (MD5 if the server provides a checksum, SHA1 if 409251881Speter the server doesn't) */ 410251881Speter svn_checksum_t *expected_source_checksum; 411251881Speter 412251881Speter /* Why two checksums? 413251881Speter The editor currently provides an md5 which we use to detect corruption 414251881Speter during transmission. We use the sha1 inside libsvn_wc both for pristine 415251881Speter handling and corruption detection. In the future, the editor will also 416251881Speter provide a sha1, so we may not have to calculate both, but for the time 417251881Speter being, that's the way it is. */ 418251881Speter 419289180Speter /* The calculated checksum of the text source or NULL if the actual 420251881Speter checksum is not being calculated. The checksum kind is identical to the 421251881Speter kind of expected_source_checksum. */ 422251881Speter svn_checksum_t *actual_source_checksum; 423251881Speter 424251881Speter /* The stream used to calculate the source checksums */ 425251881Speter svn_stream_t *source_checksum_stream; 426251881Speter 427251881Speter /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH. 428251881Speter This is initialized to all zeroes when the baton is created, then 429251881Speter populated with the MD5 digest of the resultant fulltext after the 430251881Speter last window is handled by the handler returned from 431251881Speter apply_textdelta(). */ 432251881Speter unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE]; 433251881Speter 434251881Speter /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for 435251881Speter eventually writing the pristine. */ 436251881Speter svn_checksum_t * new_text_base_sha1_checksum; 437251881Speter}; 438251881Speter 439251881Speter 440251881Speter/* Get an empty file in the temporary area for WRI_ABSPATH. The file will 441251881Speter not be set for automatic deletion, and the name will be returned in 442251881Speter TMP_FILENAME. 443251881Speter 444251881Speter This implementation creates a new empty file with a unique name. 445251881Speter 446251881Speter ### This is inefficient for callers that just want an empty file to read 447251881Speter ### from. There could be (and there used to be) a permanent, shared 448251881Speter ### empty file for this purpose. 449251881Speter 450251881Speter ### This is inefficient for callers that just want to reserve a unique 451251881Speter ### file name to create later. A better way may not be readily available. 452251881Speter */ 453251881Speterstatic svn_error_t * 454251881Speterget_empty_tmp_file(const char **tmp_filename, 455251881Speter svn_wc__db_t *db, 456251881Speter const char *wri_abspath, 457251881Speter apr_pool_t *result_pool, 458251881Speter apr_pool_t *scratch_pool) 459251881Speter{ 460251881Speter const char *temp_dir_abspath; 461251881Speter 462251881Speter SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath, 463251881Speter scratch_pool, scratch_pool)); 464251881Speter SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath, 465251881Speter svn_io_file_del_none, 466251881Speter scratch_pool, scratch_pool)); 467251881Speter 468251881Speter return SVN_NO_ERROR; 469251881Speter} 470251881Speter 471251881Speter/* An APR pool cleanup handler. This runs the working queue for an 472251881Speter editor baton. */ 473251881Speterstatic apr_status_t 474251881Spetercleanup_edit_baton(void *edit_baton) 475251881Speter{ 476251881Speter struct edit_baton *eb = edit_baton; 477251881Speter svn_error_t *err; 478251881Speter apr_pool_t *pool = apr_pool_parent_get(eb->pool); 479251881Speter 480251881Speter err = svn_wc__wq_run(eb->db, eb->wcroot_abspath, 481251881Speter NULL /* cancel_func */, NULL /* cancel_baton */, 482251881Speter pool); 483251881Speter 484251881Speter if (err) 485251881Speter { 486251881Speter apr_status_t apr_err = err->apr_err; 487251881Speter svn_error_clear(err); 488251881Speter return apr_err; 489251881Speter } 490251881Speter return APR_SUCCESS; 491251881Speter} 492251881Speter 493289180Speter/* Calculate the new repos_relpath for a directory or file */ 494251881Speterstatic svn_error_t * 495289180Spetercalculate_repos_relpath(const char **new_repos_relpath, 496289180Speter const char *local_abspath, 497289180Speter const char *old_repos_relpath, 498289180Speter struct edit_baton *eb, 499289180Speter struct dir_baton *pb, 500289180Speter apr_pool_t *result_pool, 501289180Speter apr_pool_t *scratch_pool) 502251881Speter{ 503289180Speter const char *name = svn_dirent_basename(local_abspath, NULL); 504251881Speter 505289180Speter /* Figure out the new_repos_relpath for this directory. */ 506289180Speter if (eb->switch_repos_relpath) 507251881Speter { 508251881Speter /* Handle switches... */ 509251881Speter 510251881Speter if (pb == NULL) 511251881Speter { 512251881Speter if (*eb->target_basename == '\0') 513251881Speter { 514251881Speter /* No parent baton and target_basename=="" means that we are 515289180Speter the target of the switch. Thus, our new_repos_relpath will be 516289180Speter the switch_repos_relpath. */ 517289180Speter *new_repos_relpath = eb->switch_repos_relpath; 518251881Speter } 519251881Speter else 520251881Speter { 521251881Speter /* This node is NOT the target of the switch (one of our 522251881Speter children is the target); therefore, it must already exist. 523251881Speter Get its old REPOS_RELPATH, as it won't be changing. */ 524289180Speter *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath); 525251881Speter } 526251881Speter } 527251881Speter else 528251881Speter { 529251881Speter /* This directory is *not* the root (has a parent). If there is 530251881Speter no grandparent, then we may have anchored at the parent, 531251881Speter and self is the target. If we match the target, then set 532289180Speter new_repos_relpath to the switch_repos_relpath. 533251881Speter 534289180Speter Otherwise, we simply extend new_repos_relpath from the parent. */ 535289180Speter 536251881Speter if (pb->parent_baton == NULL 537289180Speter && strcmp(eb->target_basename, name) == 0) 538289180Speter *new_repos_relpath = eb->switch_repos_relpath; 539251881Speter else 540289180Speter *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, 541289180Speter result_pool); 542251881Speter } 543251881Speter } 544251881Speter else /* must be an update */ 545251881Speter { 546251881Speter /* If we are adding the node, then simply extend the parent's 547251881Speter relpath for our own. */ 548289180Speter if (old_repos_relpath == NULL) 549251881Speter { 550251881Speter SVN_ERR_ASSERT(pb != NULL); 551289180Speter *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, 552289180Speter result_pool); 553251881Speter } 554251881Speter else 555251881Speter { 556289180Speter *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath); 557251881Speter } 558251881Speter } 559251881Speter 560289180Speter return SVN_NO_ERROR; 561289180Speter} 562289180Speter 563289180Speter/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton. 564289180Speter If PATH and PB are NULL, this is the root directory of the edit; in this 565289180Speter case, make the new dir baton in a subpool of EB->pool. 566289180Speter ADDING should be TRUE if we are adding this directory. */ 567289180Speterstatic svn_error_t * 568289180Spetermake_dir_baton(struct dir_baton **d_p, 569289180Speter const char *path, 570289180Speter struct edit_baton *eb, 571289180Speter struct dir_baton *pb, 572289180Speter svn_boolean_t adding, 573289180Speter apr_pool_t *scratch_pool) 574289180Speter{ 575289180Speter apr_pool_t *dir_pool; 576289180Speter struct dir_baton *d; 577289180Speter 578289180Speter if (pb != NULL) 579289180Speter dir_pool = svn_pool_create(pb->pool); 580289180Speter else 581289180Speter dir_pool = svn_pool_create(eb->pool); 582289180Speter 583289180Speter SVN_ERR_ASSERT(path || (! pb)); 584289180Speter 585289180Speter /* Okay, no easy out, so allocate and initialize a dir baton. */ 586289180Speter d = apr_pcalloc(dir_pool, sizeof(*d)); 587289180Speter 588289180Speter /* Construct the PATH and baseNAME of this directory. */ 589289180Speter if (path) 590289180Speter { 591289180Speter d->name = svn_dirent_basename(path, dir_pool); 592289180Speter SVN_ERR(path_join_under_root(&d->local_abspath, 593289180Speter pb->local_abspath, d->name, dir_pool)); 594289180Speter } 595289180Speter else 596289180Speter { 597289180Speter /* This is the root baton. */ 598289180Speter d->name = NULL; 599289180Speter d->local_abspath = eb->anchor_abspath; 600289180Speter } 601289180Speter 602251881Speter d->edit_baton = eb; 603251881Speter d->parent_baton = pb; 604251881Speter d->pool = dir_pool; 605251881Speter d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t)); 606251881Speter d->obstruction_found = FALSE; 607251881Speter d->add_existed = FALSE; 608251881Speter d->ref_count = 1; 609251881Speter d->old_revision = SVN_INVALID_REVNUM; 610251881Speter d->adding_dir = adding; 611251881Speter d->changed_rev = SVN_INVALID_REVNUM; 612289180Speter d->not_present_nodes = apr_hash_make(dir_pool); 613251881Speter 614251881Speter /* Copy some flags from the parent baton */ 615251881Speter if (pb) 616251881Speter { 617251881Speter d->skip_this = pb->skip_this; 618251881Speter d->shadowed = pb->shadowed || pb->edit_obstructed; 619251881Speter 620251881Speter /* the parent's bump info has one more referer */ 621251881Speter pb->ref_count++; 622251881Speter } 623251881Speter 624251881Speter /* The caller of this function needs to fill these in. */ 625251881Speter d->ambient_depth = svn_depth_unknown; 626251881Speter d->was_incomplete = FALSE; 627251881Speter 628251881Speter *d_p = d; 629251881Speter return SVN_NO_ERROR; 630251881Speter} 631251881Speter 632251881Speter/* Forward declarations. */ 633251881Speterstatic svn_error_t * 634251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted, 635251881Speter svn_boolean_t *ignored, 636251881Speter svn_wc__db_t *db, 637251881Speter const char *local_abspath, 638251881Speter apr_pool_t *scratch_pool); 639251881Speter 640251881Speter 641251881Speterstatic void 642251881Speterdo_notification(const struct edit_baton *eb, 643251881Speter const char *local_abspath, 644251881Speter svn_node_kind_t kind, 645251881Speter svn_wc_notify_action_t action, 646251881Speter apr_pool_t *scratch_pool) 647251881Speter{ 648251881Speter svn_wc_notify_t *notify; 649251881Speter 650251881Speter if (eb->notify_func == NULL) 651251881Speter return; 652251881Speter 653251881Speter notify = svn_wc_create_notify(local_abspath, action, scratch_pool); 654251881Speter notify->kind = kind; 655251881Speter 656251881Speter (*eb->notify_func)(eb->notify_baton, notify, scratch_pool); 657251881Speter} 658251881Speter 659251881Speter/* Decrement the directory's reference count. If it hits zero, 660251881Speter then this directory is "done". This means it is safe to clear its pool. 661251881Speter 662251881Speter In addition, when the directory is "done", we recurse to possible cleanup 663251881Speter the parent directory. 664251881Speter*/ 665251881Speterstatic svn_error_t * 666251881Spetermaybe_release_dir_info(struct dir_baton *db) 667251881Speter{ 668251881Speter db->ref_count--; 669251881Speter 670251881Speter if (!db->ref_count) 671251881Speter { 672251881Speter struct dir_baton *pb = db->parent_baton; 673251881Speter 674251881Speter svn_pool_destroy(db->pool); 675251881Speter 676251881Speter if (pb) 677251881Speter SVN_ERR(maybe_release_dir_info(pb)); 678251881Speter } 679251881Speter 680251881Speter return SVN_NO_ERROR; 681251881Speter} 682251881Speter 683251881Speter/* Per file baton. Lives in its own subpool below the pool of the parent 684251881Speter directory */ 685251881Speterstruct file_baton 686251881Speter{ 687251881Speter /* Pool specific to this file_baton. */ 688251881Speter apr_pool_t *pool; 689251881Speter 690251881Speter /* Name of this file (its entry in the directory). */ 691251881Speter const char *name; 692251881Speter 693251881Speter /* Absolute path to this file */ 694251881Speter const char *local_abspath; 695251881Speter 696251881Speter /* The repository relative path this file will correspond to. */ 697289180Speter const char *new_repos_relpath; 698251881Speter 699251881Speter /* The revision of the file before updating */ 700251881Speter svn_revnum_t old_revision; 701251881Speter 702251881Speter /* The repos_relpath before updating/switching */ 703251881Speter const char *old_repos_relpath; 704251881Speter 705251881Speter /* The global edit baton. */ 706251881Speter struct edit_baton *edit_baton; 707251881Speter 708251881Speter /* The parent directory of this file. */ 709251881Speter struct dir_baton *dir_baton; 710251881Speter 711251881Speter /* Set if updates to this directory are skipped */ 712251881Speter svn_boolean_t skip_this; 713251881Speter 714251881Speter /* Set if there was a previous notification */ 715251881Speter svn_boolean_t already_notified; 716251881Speter 717251881Speter /* Set if this file is new. */ 718251881Speter svn_boolean_t adding_file; 719251881Speter 720251881Speter /* Set if an unversioned file of the same name already existed in 721251881Speter this directory. */ 722251881Speter svn_boolean_t obstruction_found; 723251881Speter 724251881Speter /* Set if a file of the same name already exists and is 725251881Speter scheduled for addition without history. */ 726251881Speter svn_boolean_t add_existed; 727251881Speter 728251881Speter /* Set if this file is being added in the BASE layer, but is not-present 729251881Speter in the working copy (replaced, deleted, etc.). */ 730251881Speter svn_boolean_t shadowed; 731251881Speter 732251881Speter /* Set on a node when the existing node is obstructed, and the edit operation 733251881Speter continues as semi-shadowed update */ 734251881Speter svn_boolean_t edit_obstructed; 735251881Speter 736251881Speter /* The (new) changed_* information, cached to avoid retrieving it later */ 737251881Speter svn_revnum_t changed_rev; 738251881Speter apr_time_t changed_date; 739251881Speter const char *changed_author; 740251881Speter 741251881Speter /* If there are file content changes, these are the checksums of the 742251881Speter resulting new text base, which is in the pristine store, else NULL. */ 743251881Speter const svn_checksum_t *new_text_base_md5_checksum; 744251881Speter const svn_checksum_t *new_text_base_sha1_checksum; 745251881Speter 746251881Speter /* The checksum of the file before the update */ 747251881Speter const svn_checksum_t *original_checksum; 748251881Speter 749251881Speter /* An array of svn_prop_t structures, representing all the property 750251881Speter changes to be applied to this file. Once a file baton is 751251881Speter initialized, this is never NULL, but it may have zero elements. */ 752251881Speter apr_array_header_t *propchanges; 753251881Speter 754251881Speter /* For existing files, whether there are local modifications. FALSE for added 755251881Speter files */ 756251881Speter svn_boolean_t local_prop_mods; 757251881Speter 758251881Speter /* Bump information for the directory this file lives in */ 759251881Speter struct bump_dir_info *bump_info; 760251881Speter 761251881Speter /* A boolean indicating whether this node or one of its children has 762251881Speter received any 'real' changes. Used to avoid tree conflicts for simple 763251881Speter entryprop changes, like lock management */ 764251881Speter svn_boolean_t edited; 765251881Speter 766251881Speter /* The tree conflict to install once the node is really edited */ 767251881Speter svn_skel_t *edit_conflict; 768251881Speter}; 769251881Speter 770251881Speter 771251881Speter/* Make a new file baton in a subpool of PB->pool. PB is the parent baton. 772251881Speter * PATH is relative to the root of the edit. ADDING tells whether this file 773251881Speter * is being added. */ 774251881Speterstatic svn_error_t * 775251881Spetermake_file_baton(struct file_baton **f_p, 776251881Speter struct dir_baton *pb, 777251881Speter const char *path, 778251881Speter svn_boolean_t adding, 779251881Speter apr_pool_t *scratch_pool) 780251881Speter{ 781251881Speter apr_pool_t *file_pool = svn_pool_create(pb->pool); 782251881Speter struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f)); 783251881Speter 784251881Speter SVN_ERR_ASSERT(path); 785251881Speter 786251881Speter /* Make the file's on-disk name. */ 787251881Speter f->name = svn_dirent_basename(path, file_pool); 788251881Speter f->old_revision = SVN_INVALID_REVNUM; 789251881Speter SVN_ERR(path_join_under_root(&f->local_abspath, 790251881Speter pb->local_abspath, f->name, file_pool)); 791251881Speter 792251881Speter f->pool = file_pool; 793251881Speter f->edit_baton = pb->edit_baton; 794251881Speter f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t)); 795251881Speter f->bump_info = pb->bump_info; 796251881Speter f->adding_file = adding; 797251881Speter f->obstruction_found = FALSE; 798251881Speter f->add_existed = FALSE; 799251881Speter f->skip_this = pb->skip_this; 800251881Speter f->shadowed = pb->shadowed || pb->edit_obstructed; 801251881Speter f->dir_baton = pb; 802251881Speter f->changed_rev = SVN_INVALID_REVNUM; 803251881Speter 804251881Speter /* the directory has one more referer now */ 805251881Speter pb->ref_count++; 806251881Speter 807251881Speter *f_p = f; 808251881Speter return SVN_NO_ERROR; 809251881Speter} 810251881Speter 811251881Speter/* Complete a conflict skel by describing the update. 812251881Speter * 813251881Speter * LOCAL_KIND is the node kind of the tree conflict victim in the 814251881Speter * working copy. 815251881Speter * 816251881Speter * All temporary allocations are be made in SCRATCH_POOL, while allocations 817251881Speter * needed for the returned conflict struct are made in RESULT_POOL. 818251881Speter */ 819251881Speterstatic svn_error_t * 820251881Spetercomplete_conflict(svn_skel_t *conflict, 821251881Speter const struct edit_baton *eb, 822251881Speter const char *local_abspath, 823251881Speter const char *old_repos_relpath, 824251881Speter svn_revnum_t old_revision, 825251881Speter const char *new_repos_relpath, 826251881Speter svn_node_kind_t local_kind, 827251881Speter svn_node_kind_t target_kind, 828289180Speter const svn_skel_t *delete_conflict, 829251881Speter apr_pool_t *result_pool, 830251881Speter apr_pool_t *scratch_pool) 831251881Speter{ 832289180Speter const svn_wc_conflict_version_t *original_version = NULL; 833251881Speter svn_wc_conflict_version_t *target_version; 834251881Speter svn_boolean_t is_complete; 835251881Speter 836289180Speter SVN_ERR_ASSERT(new_repos_relpath); 837289180Speter 838251881Speter if (!conflict) 839251881Speter return SVN_NO_ERROR; /* Not conflicted */ 840251881Speter 841251881Speter SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict)); 842251881Speter 843251881Speter if (is_complete) 844251881Speter return SVN_NO_ERROR; /* Already completed */ 845251881Speter 846251881Speter if (old_repos_relpath) 847251881Speter original_version = svn_wc_conflict_version_create2(eb->repos_root, 848251881Speter eb->repos_uuid, 849251881Speter old_repos_relpath, 850251881Speter old_revision, 851251881Speter local_kind, 852251881Speter result_pool); 853289180Speter else if (delete_conflict) 854289180Speter { 855289180Speter const apr_array_header_t *locations; 856251881Speter 857289180Speter SVN_ERR(svn_wc__conflict_read_info(NULL, &locations, NULL, NULL, NULL, 858289180Speter eb->db, local_abspath, 859289180Speter delete_conflict, 860289180Speter scratch_pool, scratch_pool)); 861251881Speter 862289180Speter if (locations) 863289180Speter { 864289180Speter original_version = APR_ARRAY_IDX(locations, 0, 865289180Speter const svn_wc_conflict_version_t *); 866289180Speter } 867289180Speter } 868289180Speter 869289180Speter target_version = svn_wc_conflict_version_create2(eb->repos_root, 870289180Speter eb->repos_uuid, 871289180Speter new_repos_relpath, 872289180Speter *eb->target_revision, 873289180Speter target_kind, 874289180Speter result_pool); 875289180Speter 876289180Speter if (eb->switch_repos_relpath) 877251881Speter SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict, 878251881Speter original_version, 879251881Speter target_version, 880251881Speter result_pool, scratch_pool)); 881251881Speter else 882251881Speter SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict, 883251881Speter original_version, 884251881Speter target_version, 885251881Speter result_pool, scratch_pool)); 886251881Speter 887251881Speter return SVN_NO_ERROR; 888251881Speter} 889251881Speter 890251881Speter 891251881Speter/* Called when a directory is really edited, to avoid marking a 892251881Speter tree conflict on a node for a no-change edit */ 893251881Speterstatic svn_error_t * 894251881Spetermark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool) 895251881Speter{ 896251881Speter if (db->edited) 897251881Speter return SVN_NO_ERROR; 898251881Speter 899251881Speter if (db->parent_baton) 900251881Speter SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool)); 901251881Speter 902251881Speter db->edited = TRUE; 903251881Speter 904251881Speter if (db->edit_conflict) 905251881Speter { 906251881Speter /* We have a (delayed) tree conflict to install */ 907251881Speter 908251881Speter SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton, 909251881Speter db->local_abspath, 910251881Speter db->old_repos_relpath, db->old_revision, 911289180Speter db->new_repos_relpath, 912251881Speter svn_node_dir, svn_node_dir, 913289180Speter NULL, 914251881Speter db->pool, scratch_pool)); 915251881Speter SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db, 916251881Speter db->local_abspath, 917251881Speter db->edit_conflict, NULL, 918251881Speter scratch_pool)); 919251881Speter 920251881Speter do_notification(db->edit_baton, db->local_abspath, svn_node_dir, 921251881Speter svn_wc_notify_tree_conflict, scratch_pool); 922251881Speter db->already_notified = TRUE; 923251881Speter } 924251881Speter 925251881Speter return SVN_NO_ERROR; 926251881Speter} 927251881Speter 928251881Speter/* Called when a file is really edited, to avoid marking a 929251881Speter tree conflict on a node for a no-change edit */ 930251881Speterstatic svn_error_t * 931251881Spetermark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool) 932251881Speter{ 933251881Speter if (fb->edited) 934251881Speter return SVN_NO_ERROR; 935251881Speter 936251881Speter SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool)); 937251881Speter 938251881Speter fb->edited = TRUE; 939251881Speter 940251881Speter if (fb->edit_conflict) 941251881Speter { 942251881Speter /* We have a (delayed) tree conflict to install */ 943251881Speter 944251881Speter SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, 945251881Speter fb->local_abspath, fb->old_repos_relpath, 946289180Speter fb->old_revision, fb->new_repos_relpath, 947251881Speter svn_node_file, svn_node_file, 948289180Speter NULL, 949251881Speter fb->pool, scratch_pool)); 950251881Speter 951251881Speter SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db, 952251881Speter fb->local_abspath, 953251881Speter fb->edit_conflict, NULL, 954251881Speter scratch_pool)); 955251881Speter 956251881Speter do_notification(fb->edit_baton, fb->local_abspath, svn_node_file, 957251881Speter svn_wc_notify_tree_conflict, scratch_pool); 958251881Speter fb->already_notified = TRUE; 959251881Speter } 960251881Speter 961251881Speter return SVN_NO_ERROR; 962251881Speter} 963251881Speter 964251881Speter 965251881Speter/* Handle the next delta window of the file described by BATON. If it is 966251881Speter * the end (WINDOW == NULL), then check the checksum, store the text in the 967251881Speter * pristine store and write its details into BATON->fb->new_text_base_*. */ 968251881Speterstatic svn_error_t * 969251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton) 970251881Speter{ 971251881Speter struct handler_baton *hb = baton; 972251881Speter struct file_baton *fb = hb->fb; 973251881Speter svn_error_t *err; 974251881Speter 975251881Speter /* Apply this window. We may be done at that point. */ 976251881Speter err = hb->apply_handler(window, hb->apply_baton); 977251881Speter if (window != NULL && !err) 978251881Speter return SVN_NO_ERROR; 979251881Speter 980251881Speter if (hb->expected_source_checksum) 981251881Speter { 982251881Speter /* Close the stream to calculate HB->actual_source_md5_checksum. */ 983251881Speter svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream); 984251881Speter 985251881Speter if (!err2) 986251881Speter { 987251881Speter SVN_ERR_ASSERT(hb->expected_source_checksum->kind == 988251881Speter hb->actual_source_checksum->kind); 989251881Speter 990251881Speter if (!svn_checksum_match(hb->expected_source_checksum, 991251881Speter hb->actual_source_checksum)) 992251881Speter { 993251881Speter err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err, 994251881Speter _("Checksum mismatch while updating '%s':\n" 995251881Speter " expected: %s\n" 996251881Speter " actual: %s\n"), 997251881Speter svn_dirent_local_style(fb->local_abspath, hb->pool), 998251881Speter svn_checksum_to_cstring(hb->expected_source_checksum, 999251881Speter hb->pool), 1000251881Speter svn_checksum_to_cstring(hb->actual_source_checksum, 1001251881Speter hb->pool)); 1002251881Speter } 1003251881Speter } 1004251881Speter 1005251881Speter err = svn_error_compose_create(err, err2); 1006251881Speter } 1007251881Speter 1008251881Speter if (err) 1009251881Speter { 1010257936Speter /* We failed to apply the delta; clean up the temporary file if it 1011257936Speter already created by lazy_open_target(). */ 1012289180Speter if (hb->install_data) 1013257936Speter { 1014289180Speter svn_error_clear(svn_wc__db_pristine_install_abort(hb->install_data, 1015289180Speter hb->pool)); 1016257936Speter } 1017251881Speter } 1018251881Speter else 1019251881Speter { 1020251881Speter /* Tell the file baton about the new text base's checksums. */ 1021251881Speter fb->new_text_base_md5_checksum = 1022251881Speter svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool); 1023251881Speter fb->new_text_base_sha1_checksum = 1024251881Speter svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool); 1025251881Speter 1026251881Speter /* Store the new pristine text in the pristine store now. Later, in a 1027251881Speter single transaction we will update the BASE_NODE to include a 1028251881Speter reference to this pristine text's checksum. */ 1029289180Speter SVN_ERR(svn_wc__db_pristine_install(hb->install_data, 1030251881Speter fb->new_text_base_sha1_checksum, 1031251881Speter fb->new_text_base_md5_checksum, 1032251881Speter hb->pool)); 1033251881Speter } 1034251881Speter 1035251881Speter svn_pool_destroy(hb->pool); 1036251881Speter 1037251881Speter return err; 1038251881Speter} 1039251881Speter 1040251881Speter 1041251881Speter/* Find the last-change info within ENTRY_PROPS, and return then in the 1042251881Speter CHANGED_* parameters. Each parameter will be initialized to its "none" 1043251881Speter value, and will contain the relavent info if found. 1044251881Speter 1045251881Speter CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be 1046251881Speter used for some temporary allocations. 1047251881Speter*/ 1048251881Speterstatic svn_error_t * 1049251881Speteraccumulate_last_change(svn_revnum_t *changed_rev, 1050251881Speter apr_time_t *changed_date, 1051251881Speter const char **changed_author, 1052251881Speter const apr_array_header_t *entry_props, 1053251881Speter apr_pool_t *result_pool, 1054251881Speter apr_pool_t *scratch_pool) 1055251881Speter{ 1056251881Speter int i; 1057251881Speter 1058251881Speter *changed_rev = SVN_INVALID_REVNUM; 1059251881Speter *changed_date = 0; 1060251881Speter *changed_author = NULL; 1061251881Speter 1062251881Speter for (i = 0; i < entry_props->nelts; ++i) 1063251881Speter { 1064251881Speter const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t); 1065251881Speter 1066251881Speter /* A prop value of NULL means the information was not 1067251881Speter available. We don't remove this field from the entries 1068251881Speter file; we have convention just leave it empty. So let's 1069251881Speter just skip those entry props that have no values. */ 1070251881Speter if (! prop->value) 1071251881Speter continue; 1072251881Speter 1073251881Speter if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR)) 1074251881Speter *changed_author = apr_pstrdup(result_pool, prop->value->data); 1075251881Speter else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV)) 1076251881Speter { 1077251881Speter apr_int64_t rev; 1078251881Speter SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data)); 1079251881Speter *changed_rev = (svn_revnum_t)rev; 1080251881Speter } 1081251881Speter else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE)) 1082251881Speter SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data, 1083251881Speter scratch_pool)); 1084251881Speter 1085251881Speter /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID 1086251881Speter property here. */ 1087251881Speter } 1088251881Speter 1089251881Speter return SVN_NO_ERROR; 1090251881Speter} 1091251881Speter 1092251881Speter 1093251881Speter/* Join ADD_PATH to BASE_PATH. If ADD_PATH is absolute, or if any ".." 1094251881Speter * component of it resolves to a path above BASE_PATH, then return 1095251881Speter * SVN_ERR_WC_OBSTRUCTED_UPDATE. 1096251881Speter * 1097251881Speter * This is to prevent the situation where the repository contains, 1098251881Speter * say, "..\nastyfile". Although that's perfectly legal on some 1099251881Speter * systems, when checked out onto Win32 it would cause "nastyfile" to 1100251881Speter * be created in the parent of the current edit directory. 1101251881Speter * 1102251881Speter * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846) 1103251881Speter */ 1104251881Speterstatic svn_error_t * 1105251881Speterpath_join_under_root(const char **result_path, 1106251881Speter const char *base_path, 1107251881Speter const char *add_path, 1108251881Speter apr_pool_t *pool) 1109251881Speter{ 1110251881Speter svn_boolean_t under_root; 1111251881Speter 1112251881Speter SVN_ERR(svn_dirent_is_under_root(&under_root, 1113251881Speter result_path, base_path, add_path, pool)); 1114251881Speter 1115251881Speter if (! under_root) 1116251881Speter { 1117251881Speter return svn_error_createf( 1118251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 1119251881Speter _("Path '%s' is not in the working copy"), 1120251881Speter svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool), 1121251881Speter pool)); 1122251881Speter } 1123251881Speter 1124251881Speter /* This catches issue #3288 */ 1125251881Speter if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0) 1126251881Speter { 1127251881Speter return svn_error_createf( 1128251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 1129251881Speter _("'%s' is not valid as filename in directory '%s'"), 1130251881Speter svn_dirent_local_style(add_path, pool), 1131251881Speter svn_dirent_local_style(base_path, pool)); 1132251881Speter } 1133251881Speter 1134251881Speter return SVN_NO_ERROR; 1135251881Speter} 1136251881Speter 1137251881Speter 1138251881Speter/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/ 1139251881Speter 1140251881Speter/* An svn_delta_editor_t function. */ 1141251881Speterstatic svn_error_t * 1142251881Speterset_target_revision(void *edit_baton, 1143251881Speter svn_revnum_t target_revision, 1144251881Speter apr_pool_t *pool) 1145251881Speter{ 1146251881Speter struct edit_baton *eb = edit_baton; 1147251881Speter 1148251881Speter *(eb->target_revision) = target_revision; 1149251881Speter return SVN_NO_ERROR; 1150251881Speter} 1151251881Speter 1152251881Speter/* An svn_delta_editor_t function. */ 1153251881Speterstatic svn_error_t * 1154251881Speteropen_root(void *edit_baton, 1155251881Speter svn_revnum_t base_revision, /* This is ignored in co */ 1156251881Speter apr_pool_t *pool, 1157251881Speter void **dir_baton) 1158251881Speter{ 1159251881Speter struct edit_baton *eb = edit_baton; 1160251881Speter struct dir_baton *db; 1161251881Speter svn_boolean_t already_conflicted, conflict_ignored; 1162251881Speter svn_error_t *err; 1163251881Speter svn_wc__db_status_t status; 1164251881Speter svn_wc__db_status_t base_status; 1165251881Speter svn_node_kind_t kind; 1166251881Speter svn_boolean_t have_work; 1167251881Speter 1168251881Speter /* Note that something interesting is actually happening in this 1169251881Speter edit run. */ 1170251881Speter eb->root_opened = TRUE; 1171251881Speter 1172251881Speter SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool)); 1173251881Speter *dir_baton = db; 1174251881Speter 1175251881Speter err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored, 1176251881Speter eb->db, db->local_abspath, pool); 1177251881Speter 1178251881Speter if (err) 1179251881Speter { 1180251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1181251881Speter return svn_error_trace(err); 1182251881Speter 1183251881Speter svn_error_clear(err); 1184251881Speter already_conflicted = conflict_ignored = FALSE; 1185251881Speter } 1186251881Speter else if (already_conflicted) 1187251881Speter { 1188251881Speter /* Record a skip of both the anchor and target in the skipped tree 1189251881Speter as the anchor itself might not be updated */ 1190251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 1191251881Speter SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool)); 1192251881Speter 1193251881Speter db->skip_this = TRUE; 1194251881Speter db->already_notified = TRUE; 1195251881Speter 1196251881Speter /* Notify that we skipped the target, while we actually skipped 1197251881Speter the anchor */ 1198251881Speter do_notification(eb, eb->target_abspath, svn_node_unknown, 1199251881Speter svn_wc_notify_skip_conflicted, pool); 1200251881Speter 1201251881Speter return SVN_NO_ERROR; 1202251881Speter } 1203251881Speter 1204251881Speter 1205251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision, 1206251881Speter &db->old_repos_relpath, NULL, NULL, 1207251881Speter &db->changed_rev, &db->changed_date, 1208251881Speter &db->changed_author, &db->ambient_depth, 1209251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1210251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1211251881Speter NULL, NULL, &have_work, 1212251881Speter eb->db, db->local_abspath, 1213251881Speter db->pool, pool)); 1214251881Speter 1215289180Speter if (have_work) 1216251881Speter { 1217289180Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, 1218289180Speter &db->old_revision, 1219289180Speter &db->old_repos_relpath, NULL, NULL, 1220289180Speter &db->changed_rev, &db->changed_date, 1221289180Speter &db->changed_author, 1222289180Speter &db->ambient_depth, 1223289180Speter NULL, NULL, NULL, NULL, NULL, NULL, 1224289180Speter eb->db, db->local_abspath, 1225289180Speter db->pool, pool)); 1226251881Speter } 1227289180Speter else 1228289180Speter base_status = status; 1229289180Speter 1230289180Speter SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, 1231289180Speter db->old_repos_relpath, eb, NULL, 1232289180Speter db->pool, pool)); 1233289180Speter 1234289180Speter if (conflict_ignored) 1235289180Speter db->shadowed = TRUE; 1236251881Speter else if (have_work) 1237251881Speter { 1238362181Sdim const char *move_dst_op_root_abspath; 1239251881Speter const char *move_src_root_abspath; 1240251881Speter 1241362181Sdim SVN_ERR(svn_wc__db_base_moved_to(NULL, &move_dst_op_root_abspath, 1242362181Sdim &move_src_root_abspath, 1243251881Speter NULL, eb->db, db->local_abspath, 1244251881Speter pool, pool)); 1245251881Speter 1246251881Speter if (move_src_root_abspath) 1247251881Speter { 1248251881Speter /* This is an update anchored inside a move. We need to 1249251881Speter raise a move-edit tree-conflict on the move root to 1250251881Speter update the move destination. */ 1251251881Speter svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool); 1252251881Speter 1253251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 1254251881Speter tree_conflict, eb->db, move_src_root_abspath, 1255251881Speter svn_wc_conflict_reason_moved_away, 1256251881Speter svn_wc_conflict_action_edit, 1257362181Sdim move_src_root_abspath, 1258362181Sdim move_dst_op_root_abspath, pool, pool)); 1259251881Speter 1260251881Speter if (strcmp(db->local_abspath, move_src_root_abspath)) 1261251881Speter { 1262251881Speter /* We are raising the tree-conflict on some parent of 1263251881Speter the edit root, we won't be handling that path again 1264251881Speter so raise the conflict now. */ 1265251881Speter SVN_ERR(complete_conflict(tree_conflict, eb, 1266251881Speter move_src_root_abspath, 1267251881Speter db->old_repos_relpath, 1268289180Speter db->old_revision, 1269289180Speter db->new_repos_relpath, 1270251881Speter svn_node_dir, svn_node_dir, 1271289180Speter NULL, pool, pool)); 1272251881Speter SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, 1273251881Speter move_src_root_abspath, 1274251881Speter tree_conflict, 1275251881Speter NULL, pool)); 1276251881Speter do_notification(eb, move_src_root_abspath, svn_node_dir, 1277251881Speter svn_wc_notify_tree_conflict, pool); 1278251881Speter } 1279251881Speter else 1280251881Speter db->edit_conflict = tree_conflict; 1281251881Speter } 1282251881Speter 1283251881Speter db->shadowed = TRUE; /* Needed for the close_directory() on the root, to 1284251881Speter make sure it doesn't use the ACTUAL tree */ 1285251881Speter } 1286251881Speter 1287251881Speter if (*eb->target_basename == '\0') 1288251881Speter { 1289251881Speter /* For an update with a NULL target, this is equivalent to open_dir(): */ 1290251881Speter 1291251881Speter db->was_incomplete = (base_status == svn_wc__db_status_incomplete); 1292251881Speter 1293251881Speter /* ### TODO: Add some tree conflict and obstruction detection, etc. like 1294251881Speter open_directory() does. 1295251881Speter (or find a way to reuse that code here) 1296251881Speter 1297251881Speter ### BH 2013: I don't think we need all of the detection here, as the 1298251881Speter user explicitly asked to update this node. So we don't 1299251881Speter have to tell that it is a local replacement/delete. 1300251881Speter */ 1301251881Speter 1302251881Speter SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, 1303251881Speter db->local_abspath, 1304289180Speter db->new_repos_relpath, 1305251881Speter *eb->target_revision, 1306251881Speter pool)); 1307251881Speter } 1308251881Speter 1309251881Speter return SVN_NO_ERROR; 1310251881Speter} 1311251881Speter 1312251881Speter 1313251881Speter/* ===================================================================== */ 1314251881Speter/* Checking for local modifications. */ 1315251881Speter 1316251881Speter/* Indicates an unset svn_wc_conflict_reason_t. */ 1317251881Speter#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1) 1318251881Speter 1319251881Speter/* Check whether the incoming change ACTION on FULL_PATH would conflict with 1320251881Speter * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with 1321251881Speter * LOCAL_ABSPATH as the victim. 1322251881Speter * 1323251881Speter * The edit baton EB gives information including whether the operation is 1324251881Speter * an update or a switch. 1325251881Speter * 1326251881Speter * WORKING_STATUS is the current node status of LOCAL_ABSPATH 1327251881Speter * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists 1328251881Speter * for this node. In that case the on disk type is compared to EXPECTED_KIND. 1329251881Speter * 1330251881Speter * If a tree conflict reason was found for the incoming action, the resulting 1331251881Speter * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL, 1332251881Speter * while *PCONFLICT is always overwritten. 1333251881Speter * 1334251881Speter * The tree conflict is allocated in RESULT_POOL. Temporary allocations use 1335251881Speter * SCRATCH_POOL. 1336251881Speter */ 1337251881Speterstatic svn_error_t * 1338251881Spetercheck_tree_conflict(svn_skel_t **pconflict, 1339251881Speter struct edit_baton *eb, 1340251881Speter const char *local_abspath, 1341251881Speter svn_wc__db_status_t working_status, 1342251881Speter svn_boolean_t exists_in_repos, 1343251881Speter svn_node_kind_t expected_kind, 1344251881Speter svn_wc_conflict_action_t action, 1345251881Speter apr_pool_t *result_pool, 1346251881Speter apr_pool_t *scratch_pool) 1347251881Speter{ 1348251881Speter svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE; 1349251881Speter svn_boolean_t modified = FALSE; 1350251881Speter const char *move_src_op_root_abspath = NULL; 1351362181Sdim const char *move_dst_op_root_abspath = NULL; 1352251881Speter 1353251881Speter *pconflict = NULL; 1354251881Speter 1355251881Speter /* Find out if there are any local changes to this node that may 1356251881Speter * be the "reason" of a tree-conflict with the incoming "action". */ 1357251881Speter switch (working_status) 1358251881Speter { 1359251881Speter case svn_wc__db_status_added: 1360251881Speter case svn_wc__db_status_moved_here: 1361251881Speter case svn_wc__db_status_copied: 1362251881Speter if (!exists_in_repos) 1363251881Speter { 1364251881Speter /* The node is locally added, and it did not exist before. This 1365251881Speter * is an 'update', so the local add can only conflict with an 1366251881Speter * incoming 'add'. In fact, if we receive anything else than an 1367251881Speter * svn_wc_conflict_action_add (which includes 'added', 1368251881Speter * 'copied-here' and 'moved-here') during update on a node that 1369251881Speter * did not exist before, then something is very wrong. 1370251881Speter * Note that if there was no action on the node, this code 1371251881Speter * would not have been called in the first place. */ 1372251881Speter SVN_ERR_ASSERT(action == svn_wc_conflict_action_add); 1373251881Speter 1374251881Speter /* Scan the addition in case our caller didn't. */ 1375251881Speter if (working_status == svn_wc__db_status_added) 1376251881Speter SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL, 1377251881Speter NULL, NULL, NULL, NULL, 1378251881Speter NULL, NULL, 1379251881Speter eb->db, local_abspath, 1380251881Speter scratch_pool, scratch_pool)); 1381251881Speter 1382251881Speter if (working_status == svn_wc__db_status_moved_here) 1383251881Speter reason = svn_wc_conflict_reason_moved_here; 1384251881Speter else 1385251881Speter reason = svn_wc_conflict_reason_added; 1386251881Speter } 1387251881Speter else 1388251881Speter { 1389289180Speter /* The node is locally replaced but could also be moved-away, 1390289180Speter but we can't report that it is moved away and replaced. 1391289180Speter 1392289180Speter And we wouldn't be able to store that each of a dozen 1393289180Speter descendants was moved to other locations... 1394289180Speter 1395289180Speter Replaced is what actually happened... */ 1396289180Speter 1397289180Speter reason = svn_wc_conflict_reason_replaced; 1398251881Speter } 1399251881Speter break; 1400251881Speter 1401251881Speter 1402251881Speter case svn_wc__db_status_deleted: 1403251881Speter { 1404362181Sdim SVN_ERR(svn_wc__db_base_moved_to(NULL, &move_dst_op_root_abspath, 1405362181Sdim NULL, &move_src_op_root_abspath, 1406251881Speter eb->db, local_abspath, 1407251881Speter scratch_pool, scratch_pool)); 1408251881Speter if (move_src_op_root_abspath) 1409251881Speter reason = svn_wc_conflict_reason_moved_away; 1410251881Speter else 1411251881Speter reason = svn_wc_conflict_reason_deleted; 1412251881Speter } 1413251881Speter break; 1414251881Speter 1415251881Speter case svn_wc__db_status_incomplete: 1416251881Speter /* We used svn_wc__db_read_info(), so 'incomplete' means 1417251881Speter * - there is no node in the WORKING tree 1418251881Speter * - a BASE node is known to exist 1419251881Speter * So the node exists and is essentially 'normal'. We still need to 1420251881Speter * check prop and text mods, and those checks will retrieve the 1421251881Speter * missing information (hopefully). */ 1422251881Speter case svn_wc__db_status_normal: 1423251881Speter if (action == svn_wc_conflict_action_edit) 1424251881Speter { 1425251881Speter /* An edit onto a local edit or onto *no* local changes is no 1426251881Speter * tree-conflict. (It's possibly a text- or prop-conflict, 1427251881Speter * but we don't handle those here.) 1428251881Speter * 1429251881Speter * Except when there is a local obstruction 1430251881Speter */ 1431251881Speter if (exists_in_repos) 1432251881Speter { 1433251881Speter svn_node_kind_t disk_kind; 1434251881Speter 1435251881Speter SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, 1436251881Speter scratch_pool)); 1437251881Speter 1438251881Speter if (disk_kind != expected_kind && disk_kind != svn_node_none) 1439251881Speter { 1440251881Speter reason = svn_wc_conflict_reason_obstructed; 1441251881Speter break; 1442251881Speter } 1443251881Speter 1444251881Speter } 1445251881Speter return SVN_NO_ERROR; 1446251881Speter } 1447251881Speter 1448251881Speter /* Replace is handled as delete and then specifically in 1449251881Speter add_directory() and add_file(), so we only expect deletes here */ 1450251881Speter SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete); 1451251881Speter 1452251881Speter /* Check if the update wants to delete or replace a locally 1453251881Speter * modified node. */ 1454251881Speter 1455251881Speter 1456251881Speter /* Do a deep tree detection of local changes. The update editor will 1457251881Speter * not visit the subdirectories of a directory that it wants to delete. 1458251881Speter * Therefore, we need to start a separate crawl here. */ 1459251881Speter 1460289180Speter SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL, 1461362181Sdim eb->db, local_abspath, TRUE, 1462251881Speter eb->cancel_func, eb->cancel_baton, 1463251881Speter scratch_pool)); 1464251881Speter 1465251881Speter if (modified) 1466251881Speter { 1467289180Speter if (working_status == svn_wc__db_status_deleted) 1468251881Speter reason = svn_wc_conflict_reason_deleted; 1469251881Speter else 1470251881Speter reason = svn_wc_conflict_reason_edited; 1471251881Speter } 1472251881Speter break; 1473251881Speter 1474251881Speter case svn_wc__db_status_server_excluded: 1475251881Speter /* Not allowed to view the node. Not allowed to report tree 1476251881Speter * conflicts. */ 1477251881Speter case svn_wc__db_status_excluded: 1478251881Speter /* Locally marked as excluded. No conflicts wanted. */ 1479251881Speter case svn_wc__db_status_not_present: 1480251881Speter /* A committed delete (but parent not updated). The delete is 1481251881Speter committed, so no conflict possible during update. */ 1482251881Speter return SVN_NO_ERROR; 1483251881Speter 1484251881Speter case svn_wc__db_status_base_deleted: 1485251881Speter /* An internal status. Should never show up here. */ 1486251881Speter SVN_ERR_MALFUNCTION(); 1487251881Speter break; 1488251881Speter 1489251881Speter } 1490251881Speter 1491251881Speter if (reason == SVN_WC_CONFLICT_REASON_NONE) 1492251881Speter /* No conflict with the current action. */ 1493251881Speter return SVN_NO_ERROR; 1494251881Speter 1495251881Speter 1496251881Speter /* Sanity checks. Note that if there was no action on the node, this function 1497251881Speter * would not have been called in the first place.*/ 1498251881Speter if (reason == svn_wc_conflict_reason_edited 1499251881Speter || reason == svn_wc_conflict_reason_obstructed 1500251881Speter || reason == svn_wc_conflict_reason_deleted 1501251881Speter || reason == svn_wc_conflict_reason_moved_away 1502251881Speter || reason == svn_wc_conflict_reason_replaced) 1503251881Speter { 1504251881Speter /* When the node existed before (it was locally deleted, replaced or 1505251881Speter * edited), then 'update' cannot add it "again". So it can only send 1506251881Speter * _action_edit, _delete or _replace. */ 1507251881Speter if (action != svn_wc_conflict_action_edit 1508251881Speter && action != svn_wc_conflict_action_delete 1509251881Speter && action != svn_wc_conflict_action_replace) 1510251881Speter return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 1511251881Speter _("Unexpected attempt to add a node at path '%s'"), 1512251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 1513251881Speter } 1514251881Speter else if (reason == svn_wc_conflict_reason_added || 1515251881Speter reason == svn_wc_conflict_reason_moved_here) 1516251881Speter { 1517251881Speter /* When the node did not exist before (it was locally added), 1518251881Speter * then 'update' cannot want to modify it in any way. 1519251881Speter * It can only send _action_add. */ 1520251881Speter if (action != svn_wc_conflict_action_add) 1521251881Speter return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 1522251881Speter _("Unexpected attempt to edit, delete, or replace " 1523251881Speter "a node at path '%s'"), 1524251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 1525251881Speter 1526251881Speter } 1527251881Speter 1528251881Speter 1529251881Speter /* A conflict was detected. Create a conflict skel to record it. */ 1530251881Speter *pconflict = svn_wc__conflict_skel_create(result_pool); 1531251881Speter 1532251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict, 1533251881Speter eb->db, local_abspath, 1534251881Speter reason, 1535251881Speter action, 1536251881Speter move_src_op_root_abspath, 1537362181Sdim move_dst_op_root_abspath, 1538251881Speter result_pool, scratch_pool)); 1539251881Speter 1540251881Speter return SVN_NO_ERROR; 1541251881Speter} 1542251881Speter 1543251881Speter 1544251881Speter/* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is 1545251881Speter * not a moved-away-edit conflict, set *CONFLICTED to TRUE. Otherwise 1546251881Speter * set *CONFLICTED to FALSE. 1547251881Speter */ 1548251881Speterstatic svn_error_t * 1549251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted, 1550251881Speter svn_boolean_t *ignored, 1551251881Speter svn_wc__db_t *db, 1552251881Speter const char *local_abspath, 1553251881Speter apr_pool_t *scratch_pool) 1554251881Speter{ 1555251881Speter const char *ancestor_abspath = local_abspath; 1556251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1557251881Speter 1558251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 1559251881Speter 1560251881Speter *conflicted = *ignored = FALSE; 1561251881Speter 1562251881Speter while (TRUE) 1563251881Speter { 1564251881Speter svn_boolean_t is_wc_root; 1565251881Speter 1566251881Speter svn_pool_clear(iterpool); 1567251881Speter 1568251881Speter SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db, 1569251881Speter ancestor_abspath, TRUE, 1570251881Speter scratch_pool)); 1571251881Speter if (*conflicted || *ignored) 1572251881Speter break; 1573251881Speter 1574251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath, 1575251881Speter iterpool)); 1576251881Speter if (is_wc_root) 1577251881Speter break; 1578251881Speter 1579251881Speter ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool); 1580251881Speter } 1581251881Speter 1582251881Speter svn_pool_destroy(iterpool); 1583251881Speter 1584251881Speter return SVN_NO_ERROR; 1585251881Speter} 1586251881Speter 1587251881Speter/* Temporary helper until the new conflict handling is in place */ 1588251881Speterstatic svn_error_t * 1589251881Speternode_already_conflicted(svn_boolean_t *conflicted, 1590251881Speter svn_boolean_t *conflict_ignored, 1591251881Speter svn_wc__db_t *db, 1592251881Speter const char *local_abspath, 1593251881Speter apr_pool_t *scratch_pool) 1594251881Speter{ 1595251881Speter SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db, 1596251881Speter local_abspath, FALSE, 1597251881Speter scratch_pool)); 1598251881Speter 1599251881Speter return SVN_NO_ERROR; 1600251881Speter} 1601251881Speter 1602251881Speter 1603251881Speter/* An svn_delta_editor_t function. */ 1604251881Speterstatic svn_error_t * 1605251881Speterdelete_entry(const char *path, 1606251881Speter svn_revnum_t revision, 1607251881Speter void *parent_baton, 1608251881Speter apr_pool_t *pool) 1609251881Speter{ 1610251881Speter struct dir_baton *pb = parent_baton; 1611251881Speter struct edit_baton *eb = pb->edit_baton; 1612251881Speter const char *base = svn_relpath_basename(path, NULL); 1613251881Speter const char *local_abspath; 1614251881Speter const char *repos_relpath; 1615289180Speter const char *deleted_repos_relpath; 1616289180Speter svn_node_kind_t kind; 1617251881Speter svn_revnum_t old_revision; 1618251881Speter svn_boolean_t conflicted; 1619251881Speter svn_boolean_t have_work; 1620251881Speter svn_skel_t *tree_conflict = NULL; 1621251881Speter svn_wc__db_status_t status; 1622251881Speter svn_wc__db_status_t base_status; 1623251881Speter apr_pool_t *scratch_pool; 1624251881Speter svn_boolean_t deleting_target; 1625251881Speter svn_boolean_t deleting_switched; 1626251881Speter 1627251881Speter if (pb->skip_this) 1628251881Speter return SVN_NO_ERROR; 1629251881Speter 1630251881Speter scratch_pool = svn_pool_create(pb->pool); 1631251881Speter 1632251881Speter SVN_ERR(mark_directory_edited(pb, scratch_pool)); 1633251881Speter 1634251881Speter SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base, 1635251881Speter scratch_pool)); 1636251881Speter 1637251881Speter deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0); 1638251881Speter 1639251881Speter /* Detect obstructing working copies */ 1640251881Speter { 1641251881Speter svn_boolean_t is_root; 1642251881Speter 1643289180Speter 1644251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath, 1645251881Speter scratch_pool)); 1646251881Speter 1647251881Speter if (is_root) 1648251881Speter { 1649251881Speter /* Just skip this node; a future update will handle it */ 1650251881Speter SVN_ERR(remember_skipped_tree(eb, local_abspath, pool)); 1651251881Speter do_notification(eb, local_abspath, svn_node_unknown, 1652251881Speter svn_wc_notify_update_skip_obstruction, scratch_pool); 1653251881Speter 1654251881Speter svn_pool_destroy(scratch_pool); 1655251881Speter 1656251881Speter return SVN_NO_ERROR; 1657251881Speter } 1658251881Speter } 1659251881Speter 1660251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath, 1661251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1662251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1663251881Speter &conflicted, NULL, NULL, NULL, 1664251881Speter NULL, NULL, &have_work, 1665251881Speter eb->db, local_abspath, 1666251881Speter scratch_pool, scratch_pool)); 1667251881Speter 1668251881Speter if (!have_work) 1669251881Speter { 1670251881Speter base_status = status; 1671251881Speter } 1672251881Speter else 1673289180Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &old_revision, 1674251881Speter &repos_relpath, 1675251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1676251881Speter NULL, NULL, NULL, NULL, NULL, 1677251881Speter eb->db, local_abspath, 1678251881Speter scratch_pool, scratch_pool)); 1679251881Speter 1680251881Speter if (pb->old_repos_relpath && repos_relpath) 1681251881Speter { 1682251881Speter const char *expected_name; 1683251881Speter 1684251881Speter expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath, 1685251881Speter repos_relpath); 1686251881Speter 1687251881Speter deleting_switched = (!expected_name || strcmp(expected_name, base) != 0); 1688251881Speter } 1689251881Speter else 1690251881Speter deleting_switched = FALSE; 1691251881Speter 1692251881Speter /* Is this path a conflict victim? */ 1693251881Speter if (pb->shadowed) 1694251881Speter conflicted = FALSE; /* Conflict applies to WORKING */ 1695251881Speter else if (conflicted) 1696251881Speter SVN_ERR(node_already_conflicted(&conflicted, NULL, 1697251881Speter eb->db, local_abspath, scratch_pool)); 1698251881Speter if (conflicted) 1699251881Speter { 1700251881Speter SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool)); 1701251881Speter 1702251881Speter do_notification(eb, local_abspath, svn_node_unknown, 1703251881Speter svn_wc_notify_skip_conflicted, 1704251881Speter scratch_pool); 1705251881Speter 1706251881Speter svn_pool_destroy(scratch_pool); 1707251881Speter 1708251881Speter return SVN_NO_ERROR; 1709251881Speter } 1710251881Speter 1711251881Speter 1712251881Speter /* Receive the remote removal of excluded/server-excluded/not present node. 1713251881Speter Do not notify, but perform the change even when the node is shadowed */ 1714251881Speter if (base_status == svn_wc__db_status_not_present 1715251881Speter || base_status == svn_wc__db_status_excluded 1716251881Speter || base_status == svn_wc__db_status_server_excluded) 1717251881Speter { 1718289180Speter SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE, 1719289180Speter deleting_target, FALSE, 1720289180Speter *eb->target_revision, 1721251881Speter NULL, NULL, 1722251881Speter scratch_pool)); 1723251881Speter 1724251881Speter if (deleting_target) 1725251881Speter eb->target_deleted = TRUE; 1726251881Speter 1727251881Speter svn_pool_destroy(scratch_pool); 1728251881Speter 1729251881Speter return SVN_NO_ERROR; 1730251881Speter } 1731251881Speter 1732251881Speter /* Is this path the victim of a newly-discovered tree conflict? If so, 1733251881Speter * remember it and notify the client. Then (if it was existing and 1734251881Speter * modified), re-schedule the node to be added back again, as a (modified) 1735251881Speter * copy of the previous base version. */ 1736251881Speter 1737251881Speter /* Check for conflicts only when we haven't already recorded 1738251881Speter * a tree-conflict on a parent node. */ 1739251881Speter if (!pb->shadowed && !pb->edit_obstructed) 1740251881Speter { 1741251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, 1742251881Speter status, TRUE, 1743289180Speter kind, 1744251881Speter svn_wc_conflict_action_delete, 1745251881Speter pb->pool, scratch_pool)); 1746251881Speter } 1747251881Speter 1748251881Speter if (tree_conflict != NULL) 1749251881Speter { 1750251881Speter /* When we raise a tree conflict on a node, we don't want to mark the 1751251881Speter * node as skipped, to allow a replacement to continue doing at least 1752251881Speter * a bit of its work (possibly adding a not present node, for the 1753251881Speter * next update) */ 1754251881Speter if (!pb->deletion_conflicts) 1755251881Speter pb->deletion_conflicts = apr_hash_make(pb->pool); 1756251881Speter 1757251881Speter svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base), 1758251881Speter tree_conflict); 1759251881Speter 1760289180Speter /* Whatever the kind of conflict, we can just clear BASE 1761289180Speter by turning whatever is there into a copy */ 1762251881Speter } 1763251881Speter 1764289180Speter /* Calculate the repository-relative path of the entry which was 1765289180Speter * deleted. For updates it's the same as REPOS_RELPATH but for 1766289180Speter * switches it is within the switch target. */ 1767289180Speter SVN_ERR(calculate_repos_relpath(&deleted_repos_relpath, local_abspath, 1768289180Speter repos_relpath, eb, pb, scratch_pool, 1769289180Speter scratch_pool)); 1770251881Speter SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath, 1771289180Speter old_revision, deleted_repos_relpath, 1772289180Speter kind, svn_node_none, NULL, 1773251881Speter pb->pool, scratch_pool)); 1774251881Speter 1775251881Speter /* Issue a wq operation to delete the BASE_NODE data and to delete actual 1776251881Speter nodes based on that from disk, but leave any WORKING_NODEs on disk. 1777251881Speter 1778251881Speter Local modifications are already turned into copies at this point. 1779251881Speter 1780251881Speter If the thing being deleted is the *target* of this update, then 1781251881Speter we need to recreate a 'deleted' entry, so that the parent can give 1782251881Speter accurate reports about itself in the future. */ 1783251881Speter if (! deleting_target && ! deleting_switched) 1784251881Speter { 1785251881Speter /* Delete, and do not leave a not-present node. */ 1786251881Speter SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, 1787289180Speter (tree_conflict != NULL), 1788289180Speter FALSE, FALSE, 1789251881Speter SVN_INVALID_REVNUM /* not_present_rev */, 1790251881Speter tree_conflict, NULL, 1791251881Speter scratch_pool)); 1792251881Speter } 1793251881Speter else 1794251881Speter { 1795251881Speter /* Delete, leaving a not-present node. */ 1796251881Speter SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, 1797289180Speter (tree_conflict != NULL), 1798289180Speter TRUE, FALSE, 1799251881Speter *eb->target_revision, 1800251881Speter tree_conflict, NULL, 1801251881Speter scratch_pool)); 1802251881Speter if (deleting_target) 1803251881Speter eb->target_deleted = TRUE; 1804251881Speter else 1805251881Speter { 1806251881Speter /* Don't remove the not-present marker at the final bump */ 1807251881Speter SVN_ERR(remember_skipped_tree(eb, local_abspath, pool)); 1808251881Speter } 1809251881Speter } 1810251881Speter 1811251881Speter SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath, 1812251881Speter eb->cancel_func, eb->cancel_baton, 1813251881Speter scratch_pool)); 1814251881Speter 1815251881Speter /* Notify. */ 1816251881Speter if (tree_conflict) 1817253734Speter { 1818253734Speter if (eb->conflict_func) 1819253734Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, 1820289180Speter kind, 1821253734Speter tree_conflict, 1822253734Speter NULL /* merge_options */, 1823253734Speter eb->conflict_func, 1824253734Speter eb->conflict_baton, 1825253734Speter eb->cancel_func, 1826253734Speter eb->cancel_baton, 1827253734Speter scratch_pool)); 1828289180Speter do_notification(eb, local_abspath, kind, 1829253734Speter svn_wc_notify_tree_conflict, scratch_pool); 1830253734Speter } 1831251881Speter else 1832251881Speter { 1833251881Speter svn_wc_notify_action_t action = svn_wc_notify_update_delete; 1834251881Speter 1835251881Speter if (pb->shadowed || pb->edit_obstructed) 1836251881Speter action = svn_wc_notify_update_shadowed_delete; 1837251881Speter 1838289180Speter do_notification(eb, local_abspath, kind, action, scratch_pool); 1839251881Speter } 1840251881Speter 1841251881Speter svn_pool_destroy(scratch_pool); 1842251881Speter 1843251881Speter return SVN_NO_ERROR; 1844251881Speter} 1845251881Speter 1846251881Speter/* An svn_delta_editor_t function. */ 1847251881Speterstatic svn_error_t * 1848251881Speteradd_directory(const char *path, 1849251881Speter void *parent_baton, 1850251881Speter const char *copyfrom_path, 1851251881Speter svn_revnum_t copyfrom_rev, 1852251881Speter apr_pool_t *pool, 1853251881Speter void **child_baton) 1854251881Speter{ 1855251881Speter struct dir_baton *pb = parent_baton; 1856251881Speter struct edit_baton *eb = pb->edit_baton; 1857251881Speter struct dir_baton *db; 1858289180Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 1859251881Speter svn_node_kind_t kind; 1860251881Speter svn_wc__db_status_t status; 1861251881Speter svn_node_kind_t wc_kind; 1862251881Speter svn_boolean_t conflicted; 1863251881Speter svn_boolean_t conflict_ignored = FALSE; 1864251881Speter svn_boolean_t versioned_locally_and_present; 1865251881Speter svn_skel_t *tree_conflict = NULL; 1866251881Speter svn_error_t *err; 1867251881Speter 1868251881Speter SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev))); 1869251881Speter 1870251881Speter SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool)); 1871251881Speter *child_baton = db; 1872251881Speter 1873251881Speter if (db->skip_this) 1874251881Speter return SVN_NO_ERROR; 1875251881Speter 1876289180Speter SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, 1877289180Speter NULL, eb, pb, db->pool, scratch_pool)); 1878289180Speter 1879251881Speter SVN_ERR(mark_directory_edited(db, pool)); 1880251881Speter 1881251881Speter if (strcmp(eb->target_abspath, db->local_abspath) == 0) 1882251881Speter { 1883251881Speter /* The target of the edit is being added, give it the requested 1884251881Speter depth of the edit (but convert svn_depth_unknown to 1885251881Speter svn_depth_infinity). */ 1886251881Speter db->ambient_depth = (eb->requested_depth == svn_depth_unknown) 1887251881Speter ? svn_depth_infinity : eb->requested_depth; 1888251881Speter } 1889251881Speter else if (eb->requested_depth == svn_depth_immediates 1890251881Speter || (eb->requested_depth == svn_depth_unknown 1891251881Speter && pb->ambient_depth == svn_depth_immediates)) 1892251881Speter { 1893251881Speter db->ambient_depth = svn_depth_empty; 1894251881Speter } 1895251881Speter else 1896251881Speter { 1897251881Speter db->ambient_depth = svn_depth_infinity; 1898251881Speter } 1899251881Speter 1900251881Speter /* It may not be named the same as the administrative directory. */ 1901251881Speter if (svn_wc_is_adm_dir(db->name, pool)) 1902251881Speter return svn_error_createf( 1903251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 1904251881Speter _("Failed to add directory '%s': object of the same name as the " 1905251881Speter "administrative directory"), 1906251881Speter svn_dirent_local_style(db->local_abspath, pool)); 1907251881Speter 1908289180Speter if (!eb->clean_checkout) 1909289180Speter { 1910289180Speter SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool)); 1911251881Speter 1912289180Speter err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, 1913289180Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1914289180Speter NULL, NULL, NULL, NULL, NULL, 1915289180Speter &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, 1916289180Speter eb->db, db->local_abspath, 1917289180Speter scratch_pool, scratch_pool); 1918289180Speter } 1919289180Speter else 1920289180Speter { 1921289180Speter kind = svn_node_none; 1922289180Speter status = svn_wc__db_status_not_present; 1923289180Speter wc_kind = svn_node_unknown; 1924289180Speter conflicted = FALSE; 1925289180Speter err = NULL; 1926289180Speter } 1927289180Speter 1928251881Speter if (err) 1929251881Speter { 1930251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1931251881Speter return svn_error_trace(err); 1932251881Speter 1933251881Speter svn_error_clear(err); 1934251881Speter wc_kind = svn_node_unknown; 1935251881Speter status = svn_wc__db_status_normal; 1936251881Speter conflicted = FALSE; 1937251881Speter 1938251881Speter versioned_locally_and_present = FALSE; 1939251881Speter } 1940289180Speter else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown) 1941251881Speter { 1942289180Speter SVN_ERR_ASSERT(conflicted); 1943289180Speter versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ 1944289180Speter } 1945289180Speter else if (status == svn_wc__db_status_normal 1946289180Speter || status == svn_wc__db_status_incomplete) 1947289180Speter { 1948289180Speter svn_boolean_t root; 1949251881Speter 1950289180Speter SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath, 1951289180Speter scratch_pool)); 1952251881Speter 1953289180Speter if (root) 1954289180Speter { 1955289180Speter /* !! We found the root of a working copy obstructing the wc !! 1956251881Speter 1957289180Speter If the directory would be part of our own working copy then 1958289180Speter we wouldn't have been called as an add_directory(). 1959251881Speter 1960289180Speter The only thing we can do is add a not-present node, to allow 1961289180Speter a future update to bring in the new files when the problem is 1962289180Speter resolved. Note that svn_wc__db_base_add_not_present_node() 1963289180Speter explicitly adds the node into the parent's node database. */ 1964251881Speter 1965289180Speter svn_hash_sets(pb->not_present_nodes, 1966289180Speter apr_pstrdup(pb->pool, db->name), 1967289180Speter svn_node_kind_to_word(svn_node_dir)); 1968289180Speter } 1969289180Speter else if (wc_kind == svn_node_dir) 1970289180Speter { 1971289180Speter /* We have an editor violation. Github sometimes does this 1972289180Speter in its subversion compatibility code, when changing the 1973289180Speter depth of a working copy, or on updates from incomplete */ 1974289180Speter } 1975289180Speter else 1976289180Speter { 1977289180Speter /* We found a file external occupating the place we need in BASE. 1978251881Speter 1979289180Speter We can't add a not-present node in this case as that would overwrite 1980289180Speter the file external. Luckily the file external itself stops us from 1981289180Speter forgetting a child of this parent directory like an obstructing 1982289180Speter working copy would. 1983251881Speter 1984289180Speter The reason we get here is that the adm crawler doesn't report 1985289180Speter file externals. 1986289180Speter */ 1987289180Speter SVN_ERR_ASSERT(wc_kind == svn_node_file 1988289180Speter || wc_kind == svn_node_symlink); 1989289180Speter } 1990251881Speter 1991289180Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, scratch_pool)); 1992251881Speter db->skip_this = TRUE; 1993251881Speter db->already_notified = TRUE; 1994251881Speter 1995289180Speter do_notification(eb, db->local_abspath, wc_kind, 1996289180Speter svn_wc_notify_update_skip_obstruction, scratch_pool); 1997251881Speter 1998289180Speter svn_pool_destroy(scratch_pool); 1999289180Speter 2000251881Speter return SVN_NO_ERROR; 2001251881Speter } 2002251881Speter else 2003251881Speter versioned_locally_and_present = IS_NODE_PRESENT(status); 2004251881Speter 2005251881Speter /* Is this path a conflict victim? */ 2006251881Speter if (conflicted) 2007251881Speter { 2008251881Speter if (pb->deletion_conflicts) 2009251881Speter tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name); 2010251881Speter 2011251881Speter if (tree_conflict) 2012251881Speter { 2013251881Speter svn_wc_conflict_reason_t reason; 2014286506Speter const char *move_src_op_root_abspath; 2015362181Sdim const char *move_dst_op_root_abspath; 2016251881Speter /* So this deletion wasn't just a deletion, it is actually a 2017251881Speter replacement. Let's install a better tree conflict. */ 2018251881Speter 2019286506Speter SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, 2020286506Speter &move_src_op_root_abspath, 2021362181Sdim &move_dst_op_root_abspath, 2022251881Speter eb->db, 2023251881Speter db->local_abspath, 2024251881Speter tree_conflict, 2025289180Speter db->pool, scratch_pool)); 2026251881Speter 2027251881Speter tree_conflict = svn_wc__conflict_skel_create(db->pool); 2028251881Speter 2029251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 2030251881Speter tree_conflict, 2031251881Speter eb->db, db->local_abspath, 2032251881Speter reason, svn_wc_conflict_action_replace, 2033286506Speter move_src_op_root_abspath, 2034362181Sdim move_dst_op_root_abspath, 2035289180Speter db->pool, scratch_pool)); 2036251881Speter 2037251881Speter /* And now stop checking for conflicts here and just perform 2038251881Speter a shadowed update */ 2039251881Speter db->edit_conflict = tree_conflict; /* Cache for close_directory */ 2040251881Speter tree_conflict = NULL; /* No direct notification */ 2041251881Speter db->shadowed = TRUE; /* Just continue */ 2042251881Speter conflicted = FALSE; /* No skip */ 2043251881Speter } 2044251881Speter else 2045251881Speter SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 2046289180Speter eb->db, db->local_abspath, 2047289180Speter scratch_pool)); 2048251881Speter } 2049251881Speter 2050251881Speter /* Now the "usual" behaviour if already conflicted. Skip it. */ 2051251881Speter if (conflicted) 2052251881Speter { 2053251881Speter /* Record this conflict so that its descendants are skipped silently. */ 2054251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2055251881Speter 2056251881Speter db->skip_this = TRUE; 2057251881Speter db->already_notified = TRUE; 2058251881Speter 2059251881Speter /* We skip this node, but once the update completes the parent node will 2060251881Speter be updated to the new revision. So a future recursive update of the 2061251881Speter parent will not bring in this new node as the revision of the parent 2062251881Speter describes to the repository that all children are available. 2063251881Speter 2064251881Speter To resolve this problem, we add a not-present node to allow bringing 2065251881Speter the node in once this conflict is resolved. 2066251881Speter 2067251881Speter Note that we can safely assume that no present base node exists, 2068251881Speter because then we would not have received an add_directory. 2069251881Speter */ 2070289180Speter svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name), 2071289180Speter svn_node_kind_to_word(svn_node_dir)); 2072251881Speter 2073251881Speter do_notification(eb, db->local_abspath, svn_node_dir, 2074289180Speter svn_wc_notify_skip_conflicted, scratch_pool); 2075289180Speter 2076289180Speter svn_pool_destroy(scratch_pool); 2077251881Speter return SVN_NO_ERROR; 2078251881Speter } 2079251881Speter else if (conflict_ignored) 2080251881Speter { 2081251881Speter db->shadowed = TRUE; 2082251881Speter } 2083251881Speter 2084251881Speter if (db->shadowed) 2085251881Speter { 2086251881Speter /* Nothing to check; does not and will not exist in working copy */ 2087251881Speter } 2088251881Speter else if (versioned_locally_and_present) 2089251881Speter { 2090251881Speter /* What to do with a versioned or schedule-add dir: 2091251881Speter 2092251881Speter A dir already added without history is OK. Set add_existed 2093251881Speter so that user notification is delayed until after any prop 2094251881Speter conflicts have been found. 2095251881Speter 2096251881Speter An existing versioned dir is an error. In the future we may 2097251881Speter relax this restriction and simply update such dirs. 2098251881Speter 2099251881Speter A dir added with history is a tree conflict. */ 2100251881Speter 2101251881Speter svn_boolean_t local_is_non_dir; 2102251881Speter svn_wc__db_status_t add_status = svn_wc__db_status_normal; 2103251881Speter 2104251881Speter /* Is the local add a copy? */ 2105251881Speter if (status == svn_wc__db_status_added) 2106251881Speter SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL, 2107251881Speter NULL, NULL, NULL, NULL, 2108251881Speter eb->db, db->local_abspath, 2109289180Speter scratch_pool, scratch_pool)); 2110251881Speter 2111251881Speter 2112251881Speter /* Is there *something* that is not a dir? */ 2113251881Speter local_is_non_dir = (wc_kind != svn_node_dir 2114251881Speter && status != svn_wc__db_status_deleted); 2115251881Speter 2116251881Speter /* Do tree conflict checking if 2117251881Speter * - if there is a local copy. 2118251881Speter * - if this is a switch operation 2119251881Speter * - the node kinds mismatch 2120251881Speter * 2121251881Speter * During switch, local adds at the same path as incoming adds get 2122251881Speter * "lost" in that switching back to the original will no longer have the 2123251881Speter * local add. So switch always alerts the user with a tree conflict. */ 2124251881Speter if (!eb->adds_as_modification 2125251881Speter || local_is_non_dir 2126251881Speter || add_status != svn_wc__db_status_added) 2127251881Speter { 2128251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, 2129251881Speter db->local_abspath, 2130251881Speter status, FALSE, svn_node_none, 2131251881Speter svn_wc_conflict_action_add, 2132289180Speter db->pool, scratch_pool)); 2133251881Speter } 2134251881Speter 2135251881Speter if (tree_conflict == NULL) 2136251881Speter db->add_existed = TRUE; /* Take over WORKING */ 2137251881Speter else 2138251881Speter db->shadowed = TRUE; /* Only update BASE */ 2139251881Speter } 2140251881Speter else if (kind != svn_node_none) 2141251881Speter { 2142251881Speter /* There's an unversioned node at this path. */ 2143251881Speter db->obstruction_found = TRUE; 2144251881Speter 2145251881Speter /* Unversioned, obstructing dirs are handled by prop merge/conflict, 2146251881Speter * if unversioned obstructions are allowed. */ 2147251881Speter if (! (kind == svn_node_dir && eb->allow_unver_obstructions)) 2148251881Speter { 2149251881Speter /* Bring in the node as deleted */ /* ### Obstructed Conflict */ 2150251881Speter db->shadowed = TRUE; 2151251881Speter 2152251881Speter /* Mark a conflict */ 2153251881Speter tree_conflict = svn_wc__conflict_skel_create(db->pool); 2154251881Speter 2155251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 2156251881Speter tree_conflict, 2157251881Speter eb->db, db->local_abspath, 2158251881Speter svn_wc_conflict_reason_unversioned, 2159362181Sdim svn_wc_conflict_action_add, 2160362181Sdim NULL, NULL, db->pool, scratch_pool)); 2161251881Speter db->edit_conflict = tree_conflict; 2162251881Speter } 2163251881Speter } 2164251881Speter 2165251881Speter if (tree_conflict) 2166251881Speter SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath, 2167251881Speter db->old_repos_relpath, db->old_revision, 2168289180Speter db->new_repos_relpath, 2169289180Speter wc_kind, svn_node_dir, 2170289180Speter pb->deletion_conflicts 2171289180Speter ? svn_hash_gets(pb->deletion_conflicts, 2172289180Speter db->name) 2173289180Speter : NULL, 2174289180Speter db->pool, scratch_pool)); 2175251881Speter 2176251881Speter SVN_ERR(svn_wc__db_base_add_incomplete_directory( 2177251881Speter eb->db, db->local_abspath, 2178289180Speter db->new_repos_relpath, 2179251881Speter eb->repos_root, 2180251881Speter eb->repos_uuid, 2181251881Speter *eb->target_revision, 2182251881Speter db->ambient_depth, 2183251881Speter (db->shadowed && db->obstruction_found), 2184251881Speter (! db->shadowed 2185251881Speter && status == svn_wc__db_status_added), 2186251881Speter tree_conflict, NULL, 2187289180Speter scratch_pool)); 2188251881Speter 2189251881Speter /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just 2190251881Speter updating the DB */ 2191251881Speter if (!db->shadowed) 2192289180Speter SVN_ERR(svn_wc__ensure_directory(db->local_abspath, scratch_pool)); 2193251881Speter 2194251881Speter if (tree_conflict != NULL) 2195251881Speter { 2196289180Speter db->edit_conflict = tree_conflict; 2197253734Speter 2198251881Speter db->already_notified = TRUE; 2199251881Speter do_notification(eb, db->local_abspath, svn_node_dir, 2200289180Speter svn_wc_notify_tree_conflict, scratch_pool); 2201251881Speter } 2202251881Speter 2203251881Speter 2204251881Speter /* If this add was obstructed by dir scheduled for addition without 2205251881Speter history let close_directory() handle the notification because there 2206251881Speter might be properties to deal with. If PATH was added inside a locally 2207251881Speter deleted tree, then suppress notification, a tree conflict was already 2208251881Speter issued. */ 2209251881Speter if (eb->notify_func && !db->already_notified && !db->add_existed) 2210251881Speter { 2211251881Speter svn_wc_notify_action_t action; 2212251881Speter 2213251881Speter if (db->shadowed) 2214251881Speter action = svn_wc_notify_update_shadowed_add; 2215251881Speter else if (db->obstruction_found || db->add_existed) 2216251881Speter action = svn_wc_notify_exists; 2217251881Speter else 2218251881Speter action = svn_wc_notify_update_add; 2219251881Speter 2220251881Speter db->already_notified = TRUE; 2221251881Speter 2222289180Speter do_notification(eb, db->local_abspath, svn_node_dir, action, 2223289180Speter scratch_pool); 2224251881Speter } 2225251881Speter 2226289180Speter svn_pool_destroy(scratch_pool); 2227289180Speter 2228251881Speter return SVN_NO_ERROR; 2229251881Speter} 2230251881Speter 2231251881Speter/* An svn_delta_editor_t function. */ 2232251881Speterstatic svn_error_t * 2233251881Speteropen_directory(const char *path, 2234251881Speter void *parent_baton, 2235251881Speter svn_revnum_t base_revision, 2236251881Speter apr_pool_t *pool, 2237251881Speter void **child_baton) 2238251881Speter{ 2239251881Speter struct dir_baton *db, *pb = parent_baton; 2240251881Speter struct edit_baton *eb = pb->edit_baton; 2241251881Speter svn_boolean_t have_work; 2242251881Speter svn_boolean_t conflicted; 2243251881Speter svn_boolean_t conflict_ignored = FALSE; 2244251881Speter svn_skel_t *tree_conflict = NULL; 2245251881Speter svn_wc__db_status_t status, base_status; 2246251881Speter svn_node_kind_t wc_kind; 2247251881Speter 2248251881Speter SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool)); 2249251881Speter *child_baton = db; 2250251881Speter 2251251881Speter if (db->skip_this) 2252251881Speter return SVN_NO_ERROR; 2253251881Speter 2254251881Speter /* Detect obstructing working copies */ 2255251881Speter { 2256251881Speter svn_boolean_t is_root; 2257251881Speter 2258251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath, 2259251881Speter pool)); 2260251881Speter 2261251881Speter if (is_root) 2262251881Speter { 2263251881Speter /* Just skip this node; a future update will handle it */ 2264251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2265251881Speter db->skip_this = TRUE; 2266251881Speter db->already_notified = TRUE; 2267251881Speter 2268251881Speter do_notification(eb, db->local_abspath, svn_node_dir, 2269251881Speter svn_wc_notify_update_skip_obstruction, pool); 2270251881Speter 2271251881Speter return SVN_NO_ERROR; 2272251881Speter } 2273251881Speter } 2274251881Speter 2275251881Speter /* We should have a write lock on every directory touched. */ 2276251881Speter SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool)); 2277251881Speter 2278251881Speter SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision, 2279251881Speter &db->old_repos_relpath, NULL, NULL, 2280251881Speter &db->changed_rev, &db->changed_date, 2281251881Speter &db->changed_author, &db->ambient_depth, 2282251881Speter NULL, NULL, NULL, NULL, 2283251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2284251881Speter &conflicted, NULL, NULL, NULL, 2285251881Speter NULL, NULL, &have_work, 2286251881Speter eb->db, db->local_abspath, 2287251881Speter db->pool, pool)); 2288251881Speter 2289251881Speter if (!have_work) 2290251881Speter base_status = status; 2291251881Speter else 2292251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision, 2293251881Speter &db->old_repos_relpath, NULL, NULL, 2294251881Speter &db->changed_rev, &db->changed_date, 2295251881Speter &db->changed_author, &db->ambient_depth, 2296251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2297251881Speter eb->db, db->local_abspath, 2298251881Speter db->pool, pool)); 2299251881Speter 2300251881Speter db->was_incomplete = (base_status == svn_wc__db_status_incomplete); 2301251881Speter 2302289180Speter SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath, 2303289180Speter db->old_repos_relpath, eb, pb, 2304289180Speter db->pool, pool)); 2305289180Speter 2306251881Speter /* Is this path a conflict victim? */ 2307251881Speter if (db->shadowed) 2308251881Speter conflicted = FALSE; /* Conflict applies to WORKING */ 2309251881Speter else if (conflicted) 2310251881Speter SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 2311251881Speter eb->db, db->local_abspath, pool)); 2312251881Speter if (conflicted) 2313251881Speter { 2314251881Speter SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool)); 2315251881Speter 2316251881Speter db->skip_this = TRUE; 2317251881Speter db->already_notified = TRUE; 2318251881Speter 2319251881Speter do_notification(eb, db->local_abspath, svn_node_unknown, 2320251881Speter svn_wc_notify_skip_conflicted, pool); 2321251881Speter 2322251881Speter return SVN_NO_ERROR; 2323251881Speter } 2324251881Speter else if (conflict_ignored) 2325251881Speter { 2326251881Speter db->shadowed = TRUE; 2327251881Speter } 2328251881Speter 2329251881Speter /* Is this path a fresh tree conflict victim? If so, skip the tree 2330251881Speter with one notification. */ 2331251881Speter 2332251881Speter /* Check for conflicts only when we haven't already recorded 2333251881Speter * a tree-conflict on a parent node. */ 2334251881Speter if (!db->shadowed) 2335251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath, 2336251881Speter status, TRUE, svn_node_dir, 2337251881Speter svn_wc_conflict_action_edit, 2338251881Speter db->pool, pool)); 2339251881Speter 2340251881Speter /* Remember the roots of any locally deleted trees. */ 2341251881Speter if (tree_conflict != NULL) 2342251881Speter { 2343251881Speter svn_wc_conflict_reason_t reason; 2344251881Speter db->edit_conflict = tree_conflict; 2345251881Speter /* Other modifications wouldn't be a tree conflict */ 2346251881Speter 2347362181Sdim SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, NULL, 2348251881Speter eb->db, db->local_abspath, 2349251881Speter tree_conflict, 2350251881Speter db->pool, db->pool)); 2351251881Speter SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted 2352251881Speter || reason == svn_wc_conflict_reason_moved_away 2353251881Speter || reason == svn_wc_conflict_reason_replaced 2354251881Speter || reason == svn_wc_conflict_reason_obstructed); 2355251881Speter 2356251881Speter /* Continue updating BASE */ 2357251881Speter if (reason == svn_wc_conflict_reason_obstructed) 2358251881Speter db->edit_obstructed = TRUE; 2359251881Speter else 2360251881Speter db->shadowed = TRUE; 2361251881Speter } 2362251881Speter 2363251881Speter /* Mark directory as being at target_revision and URL, but incomplete. */ 2364251881Speter SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath, 2365289180Speter db->new_repos_relpath, 2366251881Speter *eb->target_revision, 2367251881Speter pool)); 2368251881Speter 2369251881Speter return SVN_NO_ERROR; 2370251881Speter} 2371251881Speter 2372251881Speter 2373251881Speter/* An svn_delta_editor_t function. */ 2374251881Speterstatic svn_error_t * 2375251881Speterchange_dir_prop(void *dir_baton, 2376251881Speter const char *name, 2377251881Speter const svn_string_t *value, 2378251881Speter apr_pool_t *pool) 2379251881Speter{ 2380251881Speter svn_prop_t *propchange; 2381251881Speter struct dir_baton *db = dir_baton; 2382251881Speter 2383251881Speter if (db->skip_this) 2384251881Speter return SVN_NO_ERROR; 2385251881Speter 2386251881Speter propchange = apr_array_push(db->propchanges); 2387251881Speter propchange->name = apr_pstrdup(db->pool, name); 2388289180Speter propchange->value = svn_string_dup(value, db->pool); 2389251881Speter 2390251881Speter if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind) 2391251881Speter SVN_ERR(mark_directory_edited(db, pool)); 2392251881Speter 2393251881Speter return SVN_NO_ERROR; 2394251881Speter} 2395251881Speter 2396251881Speter/* If any of the svn_prop_t objects in PROPCHANGES represents a change 2397251881Speter to the SVN_PROP_EXTERNALS property, return that change, else return 2398251881Speter null. If PROPCHANGES contains more than one such change, return 2399251881Speter the first. */ 2400251881Speterstatic const svn_prop_t * 2401251881Speterexternals_prop_changed(const apr_array_header_t *propchanges) 2402251881Speter{ 2403251881Speter int i; 2404251881Speter 2405251881Speter for (i = 0; i < propchanges->nelts; i++) 2406251881Speter { 2407251881Speter const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t)); 2408251881Speter if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0) 2409251881Speter return p; 2410251881Speter } 2411251881Speter 2412251881Speter return NULL; 2413251881Speter} 2414251881Speter 2415251881Speter 2416251881Speter 2417251881Speter/* An svn_delta_editor_t function. */ 2418251881Speterstatic svn_error_t * 2419251881Speterclose_directory(void *dir_baton, 2420251881Speter apr_pool_t *pool) 2421251881Speter{ 2422251881Speter struct dir_baton *db = dir_baton; 2423251881Speter struct edit_baton *eb = db->edit_baton; 2424251881Speter svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown; 2425251881Speter apr_array_header_t *entry_prop_changes; 2426251881Speter apr_array_header_t *dav_prop_changes; 2427251881Speter apr_array_header_t *regular_prop_changes; 2428251881Speter apr_hash_t *base_props; 2429251881Speter apr_hash_t *actual_props; 2430251881Speter apr_hash_t *new_base_props = NULL; 2431251881Speter apr_hash_t *new_actual_props = NULL; 2432251881Speter svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM; 2433251881Speter apr_time_t new_changed_date = 0; 2434251881Speter const char *new_changed_author = NULL; 2435251881Speter apr_pool_t *scratch_pool = db->pool; 2436251881Speter svn_skel_t *all_work_items = NULL; 2437251881Speter svn_skel_t *conflict_skel = NULL; 2438251881Speter 2439251881Speter /* Skip if we're in a conflicted tree. */ 2440251881Speter if (db->skip_this) 2441251881Speter { 2442251881Speter /* Allow the parent to complete its update. */ 2443251881Speter SVN_ERR(maybe_release_dir_info(db)); 2444251881Speter 2445251881Speter return SVN_NO_ERROR; 2446251881Speter } 2447251881Speter 2448251881Speter if (db->edited) 2449251881Speter conflict_skel = db->edit_conflict; 2450251881Speter 2451251881Speter SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes, 2452251881Speter &dav_prop_changes, ®ular_prop_changes, pool)); 2453251881Speter 2454251881Speter /* Fetch the existing properties. */ 2455251881Speter if ((!db->adding_dir || db->add_existed) 2456251881Speter && !db->shadowed) 2457251881Speter { 2458251881Speter SVN_ERR(svn_wc__get_actual_props(&actual_props, 2459251881Speter eb->db, db->local_abspath, 2460251881Speter scratch_pool, scratch_pool)); 2461251881Speter } 2462251881Speter else 2463251881Speter actual_props = apr_hash_make(pool); 2464251881Speter 2465251881Speter if (db->add_existed) 2466251881Speter { 2467251881Speter /* This node already exists. Grab the current pristine properties. */ 2468251881Speter SVN_ERR(svn_wc__db_read_pristine_props(&base_props, 2469251881Speter eb->db, db->local_abspath, 2470251881Speter scratch_pool, scratch_pool)); 2471251881Speter } 2472251881Speter else if (!db->adding_dir) 2473251881Speter { 2474251881Speter /* Get the BASE properties for proper merging. */ 2475251881Speter SVN_ERR(svn_wc__db_base_get_props(&base_props, 2476251881Speter eb->db, db->local_abspath, 2477251881Speter scratch_pool, scratch_pool)); 2478251881Speter } 2479251881Speter else 2480251881Speter base_props = apr_hash_make(pool); 2481251881Speter 2482251881Speter /* An incomplete directory might have props which were supposed to be 2483251881Speter deleted but weren't. Because the server sent us all the props we're 2484251881Speter supposed to have, any previous base props not in this list must be 2485251881Speter deleted (issue #1672). */ 2486251881Speter if (db->was_incomplete) 2487251881Speter { 2488251881Speter int i; 2489251881Speter apr_hash_t *props_to_delete; 2490251881Speter apr_hash_index_t *hi; 2491251881Speter 2492251881Speter /* In a copy of the BASE props, remove every property that we see an 2493251881Speter incoming change for. The remaining unmentioned properties are those 2494251881Speter which need to be deleted. */ 2495251881Speter props_to_delete = apr_hash_copy(pool, base_props); 2496251881Speter for (i = 0; i < regular_prop_changes->nelts; i++) 2497251881Speter { 2498251881Speter const svn_prop_t *prop; 2499251881Speter prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t); 2500251881Speter svn_hash_sets(props_to_delete, prop->name, NULL); 2501251881Speter } 2502251881Speter 2503251881Speter /* Add these props to the incoming propchanges (in 2504251881Speter * regular_prop_changes). */ 2505251881Speter for (hi = apr_hash_first(pool, props_to_delete); 2506251881Speter hi != NULL; 2507251881Speter hi = apr_hash_next(hi)) 2508251881Speter { 2509289180Speter const char *propname = apr_hash_this_key(hi); 2510251881Speter svn_prop_t *prop = apr_array_push(regular_prop_changes); 2511251881Speter 2512251881Speter /* Record a deletion for PROPNAME. */ 2513251881Speter prop->name = propname; 2514251881Speter prop->value = NULL; 2515251881Speter } 2516251881Speter } 2517251881Speter 2518251881Speter /* If this directory has property changes stored up, now is the time 2519251881Speter to deal with them. */ 2520251881Speter if (regular_prop_changes->nelts) 2521251881Speter { 2522251881Speter /* If recording traversal info, then see if the 2523251881Speter SVN_PROP_EXTERNALS property on this directory changed, 2524251881Speter and record before and after for the change. */ 2525251881Speter if (eb->external_func) 2526251881Speter { 2527251881Speter const svn_prop_t *change 2528251881Speter = externals_prop_changed(regular_prop_changes); 2529251881Speter 2530251881Speter if (change) 2531251881Speter { 2532251881Speter const svn_string_t *new_val_s = change->value; 2533251881Speter const svn_string_t *old_val_s; 2534251881Speter 2535251881Speter old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS); 2536251881Speter 2537251881Speter if ((new_val_s == NULL) && (old_val_s == NULL)) 2538251881Speter ; /* No value before, no value after... so do nothing. */ 2539251881Speter else if (new_val_s && old_val_s 2540251881Speter && (svn_string_compare(old_val_s, new_val_s))) 2541251881Speter ; /* Value did not change... so do nothing. */ 2542251881Speter else if (old_val_s || new_val_s) 2543251881Speter /* something changed, record the change */ 2544251881Speter { 2545251881Speter SVN_ERR((eb->external_func)( 2546251881Speter eb->external_baton, 2547251881Speter db->local_abspath, 2548251881Speter old_val_s, 2549251881Speter new_val_s, 2550251881Speter db->ambient_depth, 2551251881Speter db->pool)); 2552251881Speter } 2553251881Speter } 2554251881Speter } 2555251881Speter 2556251881Speter if (db->shadowed) 2557251881Speter { 2558251881Speter /* We don't have a relevant actual row, but we need actual properties 2559251881Speter to allow property merging without conflicts. */ 2560251881Speter if (db->adding_dir) 2561251881Speter actual_props = apr_hash_make(scratch_pool); 2562251881Speter else 2563251881Speter actual_props = base_props; 2564251881Speter } 2565251881Speter 2566251881Speter /* Merge pending properties. */ 2567251881Speter new_base_props = svn_prop__patch(base_props, regular_prop_changes, 2568251881Speter db->pool); 2569251881Speter SVN_ERR_W(svn_wc__merge_props(&conflict_skel, 2570251881Speter &prop_state, 2571251881Speter &new_actual_props, 2572251881Speter eb->db, 2573251881Speter db->local_abspath, 2574251881Speter NULL /* use baseprops */, 2575251881Speter base_props, 2576251881Speter actual_props, 2577251881Speter regular_prop_changes, 2578251881Speter db->pool, 2579251881Speter scratch_pool), 2580251881Speter _("Couldn't do property merge")); 2581251881Speter /* After a (not-dry-run) merge, we ALWAYS have props to save. */ 2582251881Speter SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL); 2583251881Speter } 2584251881Speter 2585251881Speter SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date, 2586251881Speter &new_changed_author, entry_prop_changes, 2587251881Speter scratch_pool, scratch_pool)); 2588251881Speter 2589251881Speter /* Check if we should add some not-present markers before marking the 2590251881Speter directory complete (Issue #3569) */ 2591251881Speter { 2592289180Speter apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, 2593289180Speter db->new_repos_relpath); 2594251881Speter 2595251881Speter if (new_children != NULL) 2596251881Speter { 2597251881Speter apr_hash_index_t *hi; 2598251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2599251881Speter 2600251881Speter for (hi = apr_hash_first(scratch_pool, new_children); 2601251881Speter hi; 2602251881Speter hi = apr_hash_next(hi)) 2603251881Speter { 2604251881Speter const char *child_name; 2605251881Speter const char *child_abspath; 2606251881Speter const char *child_relpath; 2607251881Speter const svn_dirent_t *dirent; 2608251881Speter svn_wc__db_status_t status; 2609251881Speter svn_node_kind_t child_kind; 2610251881Speter svn_error_t *err; 2611251881Speter 2612251881Speter svn_pool_clear(iterpool); 2613251881Speter 2614289180Speter child_name = apr_hash_this_key(hi); 2615251881Speter child_abspath = svn_dirent_join(db->local_abspath, child_name, 2616251881Speter iterpool); 2617251881Speter 2618289180Speter dirent = apr_hash_this_val(hi); 2619251881Speter child_kind = (dirent->kind == svn_node_dir) 2620251881Speter ? svn_node_dir 2621251881Speter : svn_node_file; 2622251881Speter 2623251881Speter if (db->ambient_depth < svn_depth_immediates 2624251881Speter && child_kind == svn_node_dir) 2625251881Speter continue; /* We don't need the subdirs */ 2626251881Speter 2627251881Speter /* ### We just check if there is some node in BASE at this path */ 2628251881Speter err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, 2629251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2630251881Speter NULL, NULL, NULL, NULL, NULL, 2631251881Speter eb->db, child_abspath, 2632251881Speter iterpool, iterpool); 2633251881Speter 2634251881Speter if (!err) 2635251881Speter { 2636251881Speter svn_boolean_t is_wcroot; 2637251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath, 2638251881Speter iterpool)); 2639251881Speter 2640251881Speter if (!is_wcroot) 2641251881Speter continue; /* Everything ok... Nothing to do here */ 2642251881Speter /* Fall through to allow recovering later */ 2643251881Speter } 2644251881Speter else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2645251881Speter return svn_error_trace(err); 2646251881Speter 2647251881Speter svn_error_clear(err); 2648251881Speter 2649289180Speter child_relpath = svn_relpath_join(db->new_repos_relpath, child_name, 2650251881Speter iterpool); 2651251881Speter 2652251881Speter SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, 2653251881Speter child_abspath, 2654251881Speter child_relpath, 2655251881Speter eb->repos_root, 2656251881Speter eb->repos_uuid, 2657251881Speter *eb->target_revision, 2658251881Speter child_kind, 2659251881Speter NULL, NULL, 2660251881Speter iterpool)); 2661251881Speter } 2662251881Speter 2663251881Speter svn_pool_destroy(iterpool); 2664251881Speter } 2665251881Speter } 2666251881Speter 2667289180Speter if (apr_hash_count(db->not_present_nodes)) 2668251881Speter { 2669251881Speter apr_hash_index_t *hi; 2670251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2671251881Speter 2672251881Speter /* This should call some new function (which could also be used 2673251881Speter for new_children above) to add all the names in single 2674251881Speter transaction, but I can't even trigger it. I've tried 2675251881Speter ra_local, ra_svn, ra_neon, ra_serf and they all call 2676251881Speter close_file before close_dir. */ 2677289180Speter for (hi = apr_hash_first(scratch_pool, db->not_present_nodes); 2678251881Speter hi; 2679251881Speter hi = apr_hash_next(hi)) 2680251881Speter { 2681289180Speter const char *child = apr_hash_this_key(hi); 2682251881Speter const char *child_abspath, *child_relpath; 2683289180Speter svn_node_kind_t kind = svn_node_kind_from_word(apr_hash_this_val(hi)); 2684251881Speter 2685251881Speter svn_pool_clear(iterpool); 2686251881Speter 2687251881Speter child_abspath = svn_dirent_join(db->local_abspath, child, iterpool); 2688289180Speter child_relpath = svn_dirent_join(db->new_repos_relpath, child, iterpool); 2689251881Speter 2690251881Speter SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, 2691251881Speter child_abspath, 2692251881Speter child_relpath, 2693251881Speter eb->repos_root, 2694251881Speter eb->repos_uuid, 2695251881Speter *eb->target_revision, 2696289180Speter kind, 2697251881Speter NULL, NULL, 2698251881Speter iterpool)); 2699251881Speter } 2700251881Speter svn_pool_destroy(iterpool); 2701251881Speter } 2702251881Speter 2703251881Speter /* If this directory is merely an anchor for a targeted child, then we 2704251881Speter should not be updating the node at all. */ 2705251881Speter if (db->parent_baton == NULL 2706251881Speter && *eb->target_basename != '\0') 2707251881Speter { 2708251881Speter /* And we should not have received any changes! */ 2709251881Speter SVN_ERR_ASSERT(db->propchanges->nelts == 0); 2710251881Speter /* ... which also implies NEW_CHANGED_* are not set, 2711251881Speter and NEW_BASE_PROPS == NULL. */ 2712251881Speter } 2713251881Speter else 2714251881Speter { 2715251881Speter apr_hash_t *props; 2716251881Speter apr_array_header_t *iprops = NULL; 2717251881Speter 2718251881Speter /* ### we know a base node already exists. it was created in 2719251881Speter ### open_directory or add_directory. let's just preserve the 2720251881Speter ### existing DEPTH value, and possibly CHANGED_*. */ 2721251881Speter /* If we received any changed_* values, then use them. */ 2722251881Speter if (SVN_IS_VALID_REVNUM(new_changed_rev)) 2723251881Speter db->changed_rev = new_changed_rev; 2724251881Speter if (new_changed_date != 0) 2725251881Speter db->changed_date = new_changed_date; 2726251881Speter if (new_changed_author != NULL) 2727251881Speter db->changed_author = new_changed_author; 2728251881Speter 2729251881Speter /* If no depth is set yet, set to infinity. */ 2730251881Speter if (db->ambient_depth == svn_depth_unknown) 2731251881Speter db->ambient_depth = svn_depth_infinity; 2732251881Speter 2733251881Speter if (eb->depth_is_sticky 2734251881Speter && db->ambient_depth != eb->requested_depth) 2735251881Speter { 2736251881Speter /* After a depth upgrade the entry must reflect the new depth. 2737251881Speter Upgrading to infinity changes the depth of *all* directories, 2738251881Speter upgrading to something else only changes the target. */ 2739251881Speter 2740251881Speter if (eb->requested_depth == svn_depth_infinity 2741251881Speter || (strcmp(db->local_abspath, eb->target_abspath) == 0 2742251881Speter && eb->requested_depth > db->ambient_depth)) 2743251881Speter { 2744251881Speter db->ambient_depth = eb->requested_depth; 2745251881Speter } 2746251881Speter } 2747251881Speter 2748251881Speter /* Do we have new properties to install? Or shall we simply retain 2749251881Speter the prior set of properties? If we're installing new properties, 2750251881Speter then we also want to write them to an old-style props file. */ 2751251881Speter props = new_base_props; 2752251881Speter if (props == NULL) 2753251881Speter props = base_props; 2754251881Speter 2755251881Speter if (conflict_skel) 2756251881Speter { 2757251881Speter svn_skel_t *work_item; 2758251881Speter 2759251881Speter SVN_ERR(complete_conflict(conflict_skel, 2760251881Speter db->edit_baton, 2761251881Speter db->local_abspath, 2762251881Speter db->old_repos_relpath, 2763251881Speter db->old_revision, 2764289180Speter db->new_repos_relpath, 2765251881Speter svn_node_dir, svn_node_dir, 2766289180Speter (db->parent_baton 2767289180Speter && db->parent_baton->deletion_conflicts) 2768289180Speter ? svn_hash_gets( 2769289180Speter db->parent_baton->deletion_conflicts, 2770289180Speter db->name) 2771289180Speter : NULL, 2772251881Speter db->pool, scratch_pool)); 2773251881Speter 2774251881Speter SVN_ERR(svn_wc__conflict_create_markers(&work_item, 2775251881Speter eb->db, db->local_abspath, 2776251881Speter conflict_skel, 2777251881Speter scratch_pool, scratch_pool)); 2778251881Speter 2779251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 2780251881Speter scratch_pool); 2781251881Speter } 2782251881Speter 2783251881Speter /* Any inherited props to be set set for this base node? */ 2784251881Speter if (eb->wcroot_iprops) 2785251881Speter { 2786251881Speter iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath); 2787251881Speter 2788251881Speter /* close_edit may also update iprops for switched nodes, catching 2789251881Speter those for which close_directory is never called (e.g. a switch 2790251881Speter with no changes). So as a minor optimization we remove any 2791251881Speter iprops from the hash so as not to set them again in 2792251881Speter close_edit. */ 2793251881Speter if (iprops) 2794251881Speter svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL); 2795251881Speter } 2796251881Speter 2797251881Speter /* Update the BASE data for the directory and mark the directory 2798251881Speter complete */ 2799251881Speter SVN_ERR(svn_wc__db_base_add_directory( 2800251881Speter eb->db, db->local_abspath, 2801251881Speter eb->wcroot_abspath, 2802289180Speter db->new_repos_relpath, 2803251881Speter eb->repos_root, eb->repos_uuid, 2804251881Speter *eb->target_revision, 2805251881Speter props, 2806251881Speter db->changed_rev, db->changed_date, db->changed_author, 2807251881Speter NULL /* children */, 2808251881Speter db->ambient_depth, 2809251881Speter (dav_prop_changes->nelts > 0) 2810251881Speter ? svn_prop_array_to_hash(dav_prop_changes, pool) 2811251881Speter : NULL, 2812251881Speter (! db->shadowed) && new_base_props != NULL, 2813289180Speter new_actual_props, iprops, 2814289180Speter conflict_skel, all_work_items, 2815251881Speter scratch_pool)); 2816251881Speter } 2817251881Speter 2818251881Speter /* Process all of the queued work items for this directory. */ 2819251881Speter SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath, 2820251881Speter eb->cancel_func, eb->cancel_baton, 2821251881Speter scratch_pool)); 2822251881Speter 2823289180Speter if (db->parent_baton) 2824289180Speter svn_hash_sets(db->parent_baton->not_present_nodes, db->name, NULL); 2825289180Speter 2826251881Speter if (conflict_skel && eb->conflict_func) 2827251881Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath, 2828289180Speter svn_node_dir, 2829251881Speter conflict_skel, 2830251881Speter NULL /* merge_options */, 2831251881Speter eb->conflict_func, 2832251881Speter eb->conflict_baton, 2833251881Speter eb->cancel_func, 2834253734Speter eb->cancel_baton, 2835251881Speter scratch_pool)); 2836251881Speter 2837251881Speter /* Notify of any prop changes on this directory -- but do nothing if 2838251881Speter it's an added or skipped directory, because notification has already 2839251881Speter happened in that case - unless the add was obstructed by a dir 2840251881Speter scheduled for addition without history, in which case we handle 2841251881Speter notification here). */ 2842251881Speter if (!db->already_notified && eb->notify_func && db->edited) 2843251881Speter { 2844251881Speter svn_wc_notify_t *notify; 2845251881Speter svn_wc_notify_action_t action; 2846251881Speter 2847251881Speter if (db->shadowed || db->edit_obstructed) 2848251881Speter action = svn_wc_notify_update_shadowed_update; 2849251881Speter else if (db->obstruction_found || db->add_existed) 2850251881Speter action = svn_wc_notify_exists; 2851251881Speter else 2852251881Speter action = svn_wc_notify_update_update; 2853251881Speter 2854251881Speter notify = svn_wc_create_notify(db->local_abspath, action, pool); 2855251881Speter notify->kind = svn_node_dir; 2856251881Speter notify->prop_state = prop_state; 2857251881Speter notify->revision = *eb->target_revision; 2858251881Speter notify->old_revision = db->old_revision; 2859251881Speter 2860251881Speter eb->notify_func(eb->notify_baton, notify, scratch_pool); 2861251881Speter } 2862251881Speter 2863289180Speter if (db->edited) 2864289180Speter eb->edited = db->edited; 2865289180Speter 2866251881Speter /* We're done with this directory, so remove one reference from the 2867251881Speter bump information. */ 2868251881Speter SVN_ERR(maybe_release_dir_info(db)); 2869251881Speter 2870251881Speter return SVN_NO_ERROR; 2871251881Speter} 2872251881Speter 2873251881Speter 2874251881Speter/* Common code for 'absent_file' and 'absent_directory'. */ 2875251881Speterstatic svn_error_t * 2876251881Speterabsent_node(const char *path, 2877251881Speter svn_node_kind_t absent_kind, 2878251881Speter void *parent_baton, 2879251881Speter apr_pool_t *pool) 2880251881Speter{ 2881251881Speter struct dir_baton *pb = parent_baton; 2882251881Speter struct edit_baton *eb = pb->edit_baton; 2883251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 2884251881Speter const char *name = svn_dirent_basename(path, NULL); 2885251881Speter const char *local_abspath; 2886251881Speter svn_error_t *err; 2887251881Speter svn_wc__db_status_t status; 2888251881Speter svn_node_kind_t kind; 2889289180Speter svn_skel_t *tree_conflict = NULL; 2890251881Speter 2891251881Speter if (pb->skip_this) 2892251881Speter return SVN_NO_ERROR; 2893251881Speter 2894251881Speter local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool); 2895251881Speter /* If an item by this name is scheduled for addition that's a 2896251881Speter genuine tree-conflict. */ 2897251881Speter err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 2898251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2899251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2900251881Speter NULL, NULL, NULL, NULL, 2901251881Speter eb->db, local_abspath, 2902251881Speter scratch_pool, scratch_pool); 2903251881Speter 2904251881Speter if (err) 2905251881Speter { 2906251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2907251881Speter return svn_error_trace(err); 2908251881Speter 2909251881Speter svn_error_clear(err); 2910251881Speter status = svn_wc__db_status_not_present; 2911251881Speter kind = svn_node_unknown; 2912251881Speter } 2913251881Speter 2914309511Speter if (status != svn_wc__db_status_server_excluded) 2915309511Speter SVN_ERR(mark_directory_edited(pb, scratch_pool)); 2916309511Speter /* Else fall through as we should update the revision anyway */ 2917309511Speter 2918257936Speter if (status == svn_wc__db_status_normal) 2919251881Speter { 2920257936Speter svn_boolean_t wcroot; 2921257936Speter /* We found an obstructing working copy or a file external! */ 2922251881Speter 2923257936Speter SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath, 2924257936Speter scratch_pool)); 2925251881Speter 2926257936Speter if (wcroot) 2927257936Speter { 2928257936Speter /* 2929257936Speter We have an obstructing working copy; possibly a directory external 2930257936Speter 2931257936Speter We can do two things now: 2932257936Speter 1) notify the user, record a skip, etc. 2933257936Speter 2) Just record the absent node in BASE in the parent 2934257936Speter working copy. 2935257936Speter 2936257936Speter As option 2 happens to be exactly what we do anyway, fall through. 2937257936Speter */ 2938257936Speter } 2939257936Speter else 2940257936Speter { 2941309511Speter svn_boolean_t file_external; 2942309511Speter svn_revnum_t revnum; 2943257936Speter 2944309511Speter SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &revnum, NULL, NULL, 2945309511Speter NULL, NULL, NULL, NULL, NULL, NULL, 2946309511Speter NULL, NULL, NULL, NULL, 2947309511Speter &file_external, 2948309511Speter eb->db, local_abspath, 2949309511Speter scratch_pool, scratch_pool)); 2950257936Speter 2951309511Speter if (file_external) 2952309511Speter { 2953309511Speter /* The server asks us to replace a file external 2954309511Speter (Existing BASE node; not reported by the working copy crawler 2955309511Speter or there would have been a delete_entry() call. 2956257936Speter 2957309511Speter There is no way we can store this state in the working copy as 2958309511Speter the BASE layer is already filled. 2959309511Speter We could error out, but that is not helping anybody; the user is not 2960309511Speter even seeing with what the file external would be replaced, so let's 2961309511Speter report a skip and continue the update. 2962309511Speter */ 2963309511Speter 2964309511Speter if (eb->notify_func) 2965309511Speter { 2966309511Speter svn_wc_notify_t *notify; 2967309511Speter notify = svn_wc_create_notify( 2968257936Speter local_abspath, 2969257936Speter svn_wc_notify_update_skip_obstruction, 2970257936Speter scratch_pool); 2971257936Speter 2972309511Speter eb->notify_func(eb->notify_baton, notify, scratch_pool); 2973309511Speter } 2974309511Speter 2975309511Speter svn_pool_destroy(scratch_pool); 2976309511Speter return SVN_NO_ERROR; 2977257936Speter } 2978309511Speter else 2979309511Speter { 2980309511Speter /* We have a normal local node that will now be hidden for the 2981309511Speter user. Let's try to delete what is there. This may introduce 2982309511Speter tree conflicts if there are local changes */ 2983309511Speter SVN_ERR(delete_entry(path, revnum, pb, scratch_pool)); 2984257936Speter 2985309511Speter /* delete_entry() promises that BASE is empty after the operation, 2986309511Speter so we can just fall through now */ 2987309511Speter } 2988257936Speter } 2989251881Speter } 2990251881Speter else if (status == svn_wc__db_status_not_present 2991251881Speter || status == svn_wc__db_status_server_excluded 2992251881Speter || status == svn_wc__db_status_excluded) 2993251881Speter { 2994251881Speter /* The BASE node is not actually there, so we can safely turn it into 2995251881Speter an absent node */ 2996251881Speter } 2997251881Speter else 2998251881Speter { 2999251881Speter /* We have a local addition. If this would be a BASE node it would have 3000251881Speter been deleted before we get here. (Which might have turned it into 3001289180Speter a copy). */ 3002251881Speter SVN_ERR_ASSERT(status != svn_wc__db_status_normal); 3003251881Speter 3004289180Speter if (!pb->shadowed && !pb->edit_obstructed) 3005289180Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath, 3006289180Speter status, FALSE, svn_node_unknown, 3007289180Speter svn_wc_conflict_action_add, 3008289180Speter scratch_pool, scratch_pool)); 3009289180Speter 3010251881Speter } 3011251881Speter 3012251881Speter { 3013251881Speter const char *repos_relpath; 3014289180Speter repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, scratch_pool); 3015251881Speter 3016289180Speter if (tree_conflict) 3017289180Speter SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, 3018289180Speter NULL, SVN_INVALID_REVNUM, repos_relpath, 3019289180Speter kind, svn_node_unknown, NULL, 3020289180Speter scratch_pool, scratch_pool)); 3021289180Speter 3022251881Speter /* Insert an excluded node below the parent node to note that this child 3023251881Speter is absent. (This puts it in the parent db if the child is obstructed) */ 3024251881Speter SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath, 3025251881Speter repos_relpath, eb->repos_root, 3026251881Speter eb->repos_uuid, 3027251881Speter *(eb->target_revision), 3028251881Speter absent_kind, 3029251881Speter svn_wc__db_status_server_excluded, 3030289180Speter tree_conflict, NULL, 3031251881Speter scratch_pool)); 3032289180Speter 3033289180Speter if (tree_conflict) 3034289180Speter { 3035289180Speter if (eb->conflict_func) 3036289180Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath, 3037289180Speter kind, 3038289180Speter tree_conflict, 3039289180Speter NULL /* merge_options */, 3040289180Speter eb->conflict_func, 3041289180Speter eb->conflict_baton, 3042289180Speter eb->cancel_func, 3043289180Speter eb->cancel_baton, 3044289180Speter scratch_pool)); 3045289180Speter do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict, 3046289180Speter scratch_pool); 3047289180Speter } 3048251881Speter } 3049251881Speter 3050251881Speter svn_pool_destroy(scratch_pool); 3051251881Speter 3052251881Speter return SVN_NO_ERROR; 3053251881Speter} 3054251881Speter 3055251881Speter 3056251881Speter/* An svn_delta_editor_t function. */ 3057251881Speterstatic svn_error_t * 3058251881Speterabsent_file(const char *path, 3059251881Speter void *parent_baton, 3060251881Speter apr_pool_t *pool) 3061251881Speter{ 3062251881Speter return absent_node(path, svn_node_file, parent_baton, pool); 3063251881Speter} 3064251881Speter 3065251881Speter 3066251881Speter/* An svn_delta_editor_t function. */ 3067251881Speterstatic svn_error_t * 3068251881Speterabsent_directory(const char *path, 3069251881Speter void *parent_baton, 3070251881Speter apr_pool_t *pool) 3071251881Speter{ 3072251881Speter return absent_node(path, svn_node_dir, parent_baton, pool); 3073251881Speter} 3074251881Speter 3075251881Speter 3076251881Speter/* An svn_delta_editor_t function. */ 3077251881Speterstatic svn_error_t * 3078251881Speteradd_file(const char *path, 3079251881Speter void *parent_baton, 3080251881Speter const char *copyfrom_path, 3081251881Speter svn_revnum_t copyfrom_rev, 3082251881Speter apr_pool_t *pool, 3083251881Speter void **file_baton) 3084251881Speter{ 3085251881Speter struct dir_baton *pb = parent_baton; 3086251881Speter struct edit_baton *eb = pb->edit_baton; 3087251881Speter struct file_baton *fb; 3088289180Speter svn_node_kind_t kind; 3089289180Speter svn_node_kind_t wc_kind; 3090289180Speter svn_wc__db_status_t status; 3091251881Speter apr_pool_t *scratch_pool; 3092289180Speter svn_boolean_t conflicted; 3093251881Speter svn_boolean_t conflict_ignored = FALSE; 3094289180Speter svn_boolean_t versioned_locally_and_present; 3095251881Speter svn_skel_t *tree_conflict = NULL; 3096251881Speter svn_error_t *err = SVN_NO_ERROR; 3097251881Speter 3098251881Speter SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev))); 3099251881Speter 3100251881Speter SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool)); 3101251881Speter *file_baton = fb; 3102251881Speter 3103251881Speter if (fb->skip_this) 3104251881Speter return SVN_NO_ERROR; 3105251881Speter 3106289180Speter SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath, 3107289180Speter NULL, eb, pb, fb->pool, pool)); 3108251881Speter SVN_ERR(mark_file_edited(fb, pool)); 3109251881Speter 3110251881Speter /* The file_pool can stick around for a *long* time, so we want to 3111251881Speter use a subpool for any temporary allocations. */ 3112251881Speter scratch_pool = svn_pool_create(pool); 3113251881Speter 3114251881Speter 3115251881Speter /* It may not be named the same as the administrative directory. */ 3116251881Speter if (svn_wc_is_adm_dir(fb->name, pool)) 3117251881Speter return svn_error_createf( 3118251881Speter SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 3119251881Speter _("Failed to add file '%s': object of the same name as the " 3120251881Speter "administrative directory"), 3121251881Speter svn_dirent_local_style(fb->local_abspath, pool)); 3122251881Speter 3123251881Speter if (!eb->clean_checkout) 3124251881Speter { 3125251881Speter SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool)); 3126251881Speter 3127251881Speter err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL, 3128251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 3129251881Speter NULL, NULL, NULL, NULL, NULL, 3130251881Speter &conflicted, NULL, NULL, NULL, NULL, NULL, NULL, 3131251881Speter eb->db, fb->local_abspath, 3132251881Speter scratch_pool, scratch_pool); 3133251881Speter } 3134289180Speter else 3135289180Speter { 3136289180Speter kind = svn_node_none; 3137289180Speter status = svn_wc__db_status_not_present; 3138289180Speter wc_kind = svn_node_unknown; 3139289180Speter conflicted = FALSE; 3140289180Speter } 3141251881Speter 3142251881Speter if (err) 3143251881Speter { 3144251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 3145251881Speter return svn_error_trace(err); 3146251881Speter 3147251881Speter svn_error_clear(err); 3148251881Speter wc_kind = svn_node_unknown; 3149251881Speter conflicted = FALSE; 3150251881Speter 3151251881Speter versioned_locally_and_present = FALSE; 3152251881Speter } 3153289180Speter else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown) 3154251881Speter { 3155289180Speter SVN_ERR_ASSERT(conflicted); 3156289180Speter versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */ 3157289180Speter } 3158289180Speter else if (status == svn_wc__db_status_normal 3159289180Speter || status == svn_wc__db_status_incomplete) 3160289180Speter { 3161289180Speter svn_boolean_t root; 3162251881Speter 3163289180Speter SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath, 3164289180Speter scratch_pool)); 3165251881Speter 3166289180Speter if (root) 3167289180Speter { 3168289180Speter /* !! We found the root of a working copy obstructing the wc !! 3169251881Speter 3170289180Speter If the directory would be part of our own working copy then 3171289180Speter we wouldn't have been called as an add_directory(). 3172251881Speter 3173289180Speter The only thing we can do is add a not-present node, to allow 3174289180Speter a future update to bring in the new files when the problem is 3175289180Speter resolved. Note that svn_wc__db_base_add_not_present_node() 3176289180Speter explicitly adds the node into the parent's node database. */ 3177251881Speter 3178289180Speter svn_hash_sets(pb->not_present_nodes, 3179289180Speter apr_pstrdup(pb->pool, fb->name), 3180289180Speter svn_node_kind_to_word(svn_node_dir)); 3181289180Speter } 3182289180Speter else if (wc_kind == svn_node_dir) 3183289180Speter { 3184289180Speter /* We have an editor violation. Github sometimes does this 3185289180Speter in its subversion compatibility code, when changing the 3186289180Speter depth of a working copy, or on updates from incomplete */ 3187289180Speter } 3188289180Speter else 3189289180Speter { 3190289180Speter /* We found a file external occupating the place we need in BASE. 3191251881Speter 3192289180Speter We can't add a not-present node in this case as that would overwrite 3193289180Speter the file external. Luckily the file external itself stops us from 3194289180Speter forgetting a child of this parent directory like an obstructing 3195289180Speter working copy would. 3196251881Speter 3197289180Speter The reason we get here is that the adm crawler doesn't report 3198289180Speter file externals. 3199289180Speter */ 3200289180Speter SVN_ERR_ASSERT(wc_kind == svn_node_file 3201289180Speter || wc_kind == svn_node_symlink); 3202289180Speter } 3203251881Speter 3204251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3205251881Speter fb->skip_this = TRUE; 3206251881Speter fb->already_notified = TRUE; 3207251881Speter 3208289180Speter do_notification(eb, fb->local_abspath, wc_kind, 3209251881Speter svn_wc_notify_update_skip_obstruction, scratch_pool); 3210251881Speter 3211251881Speter svn_pool_destroy(scratch_pool); 3212251881Speter 3213251881Speter return SVN_NO_ERROR; 3214251881Speter } 3215251881Speter else 3216251881Speter versioned_locally_and_present = IS_NODE_PRESENT(status); 3217251881Speter 3218251881Speter 3219251881Speter /* Is this path a conflict victim? */ 3220251881Speter if (fb->shadowed) 3221251881Speter conflicted = FALSE; /* Conflict applies to WORKING */ 3222251881Speter else if (conflicted) 3223251881Speter { 3224251881Speter if (pb->deletion_conflicts) 3225251881Speter tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name); 3226251881Speter 3227251881Speter if (tree_conflict) 3228251881Speter { 3229251881Speter svn_wc_conflict_reason_t reason; 3230286506Speter const char *move_src_op_root_abspath; 3231362181Sdim const char *move_dst_op_root_abspath; 3232251881Speter /* So this deletion wasn't just a deletion, it is actually a 3233251881Speter replacement. Let's install a better tree conflict. */ 3234251881Speter 3235286506Speter SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, 3236286506Speter &move_src_op_root_abspath, 3237362181Sdim &move_dst_op_root_abspath, 3238251881Speter eb->db, 3239251881Speter fb->local_abspath, 3240251881Speter tree_conflict, 3241289180Speter fb->pool, scratch_pool)); 3242251881Speter 3243251881Speter tree_conflict = svn_wc__conflict_skel_create(fb->pool); 3244251881Speter 3245251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3246251881Speter tree_conflict, 3247251881Speter eb->db, fb->local_abspath, 3248251881Speter reason, svn_wc_conflict_action_replace, 3249286506Speter move_src_op_root_abspath, 3250362181Sdim move_dst_op_root_abspath, 3251289180Speter fb->pool, scratch_pool)); 3252251881Speter 3253251881Speter /* And now stop checking for conflicts here and just perform 3254251881Speter a shadowed update */ 3255251881Speter fb->edit_conflict = tree_conflict; /* Cache for close_file */ 3256251881Speter tree_conflict = NULL; /* No direct notification */ 3257251881Speter fb->shadowed = TRUE; /* Just continue */ 3258251881Speter conflicted = FALSE; /* No skip */ 3259251881Speter } 3260251881Speter else 3261251881Speter SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 3262251881Speter eb->db, fb->local_abspath, pool)); 3263251881Speter } 3264251881Speter 3265251881Speter /* Now the usual conflict handling: skip. */ 3266251881Speter if (conflicted) 3267251881Speter { 3268251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3269251881Speter 3270251881Speter fb->skip_this = TRUE; 3271251881Speter fb->already_notified = TRUE; 3272251881Speter 3273251881Speter /* We skip this node, but once the update completes the parent node will 3274251881Speter be updated to the new revision. So a future recursive update of the 3275251881Speter parent will not bring in this new node as the revision of the parent 3276251881Speter describes to the repository that all children are available. 3277251881Speter 3278251881Speter To resolve this problem, we add a not-present node to allow bringing 3279251881Speter the node in once this conflict is resolved. 3280251881Speter 3281251881Speter Note that we can safely assume that no present base node exists, 3282251881Speter because then we would not have received an add_file. 3283251881Speter */ 3284289180Speter svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name), 3285289180Speter svn_node_kind_to_word(svn_node_file)); 3286251881Speter 3287289180Speter do_notification(eb, fb->local_abspath, svn_node_file, 3288251881Speter svn_wc_notify_skip_conflicted, scratch_pool); 3289251881Speter 3290251881Speter svn_pool_destroy(scratch_pool); 3291251881Speter 3292251881Speter return SVN_NO_ERROR; 3293251881Speter } 3294251881Speter else if (conflict_ignored) 3295251881Speter { 3296251881Speter fb->shadowed = TRUE; 3297251881Speter } 3298251881Speter 3299251881Speter if (fb->shadowed) 3300251881Speter { 3301251881Speter /* Nothing to check; does not and will not exist in working copy */ 3302251881Speter } 3303251881Speter else if (versioned_locally_and_present) 3304251881Speter { 3305251881Speter /* What to do with a versioned or schedule-add file: 3306251881Speter 3307251881Speter If the UUID doesn't match the parent's, or the URL isn't a child of 3308251881Speter the parent dir's URL, it's an error. 3309251881Speter 3310251881Speter Set add_existed so that user notification is delayed until after any 3311251881Speter text or prop conflicts have been found. 3312251881Speter 3313251881Speter Whether the incoming add is a symlink or a file will only be known in 3314251881Speter close_file(), when the props are known. So with a locally added file 3315251881Speter or symlink, let close_file() check for a tree conflict. 3316251881Speter 3317251881Speter We will never see missing files here, because these would be 3318251881Speter re-added during the crawler phase. */ 3319251881Speter svn_boolean_t local_is_file; 3320251881Speter 3321251881Speter /* Is the local node a copy or move */ 3322251881Speter if (status == svn_wc__db_status_added) 3323251881Speter SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL, 3324251881Speter NULL, NULL, NULL, 3325251881Speter eb->db, fb->local_abspath, 3326251881Speter scratch_pool, scratch_pool)); 3327251881Speter 3328251881Speter /* Is there something that is a file? */ 3329251881Speter local_is_file = (wc_kind == svn_node_file 3330251881Speter || wc_kind == svn_node_symlink); 3331251881Speter 3332251881Speter /* Do tree conflict checking if 3333251881Speter * - if there is a local copy. 3334251881Speter * - if this is a switch operation 3335251881Speter * - the node kinds mismatch 3336251881Speter * 3337251881Speter * During switch, local adds at the same path as incoming adds get 3338251881Speter * "lost" in that switching back to the original will no longer have the 3339251881Speter * local add. So switch always alerts the user with a tree conflict. */ 3340251881Speter if (!eb->adds_as_modification 3341251881Speter || !local_is_file 3342251881Speter || status != svn_wc__db_status_added) 3343251881Speter { 3344251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, 3345251881Speter fb->local_abspath, 3346251881Speter status, FALSE, svn_node_none, 3347251881Speter svn_wc_conflict_action_add, 3348289180Speter fb->pool, scratch_pool)); 3349251881Speter } 3350251881Speter 3351251881Speter if (tree_conflict == NULL) 3352251881Speter fb->add_existed = TRUE; /* Take over WORKING */ 3353251881Speter else 3354251881Speter fb->shadowed = TRUE; /* Only update BASE */ 3355251881Speter 3356251881Speter } 3357251881Speter else if (kind != svn_node_none) 3358251881Speter { 3359251881Speter /* There's an unversioned node at this path. */ 3360251881Speter fb->obstruction_found = TRUE; 3361251881Speter 3362251881Speter /* Unversioned, obstructing files are handled by text merge/conflict, 3363251881Speter * if unversioned obstructions are allowed. */ 3364251881Speter if (! (kind == svn_node_file && eb->allow_unver_obstructions)) 3365251881Speter { 3366251881Speter /* Bring in the node as deleted */ /* ### Obstructed Conflict */ 3367251881Speter fb->shadowed = TRUE; 3368251881Speter 3369251881Speter /* Mark a conflict */ 3370251881Speter tree_conflict = svn_wc__conflict_skel_create(fb->pool); 3371251881Speter 3372251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3373251881Speter tree_conflict, 3374251881Speter eb->db, fb->local_abspath, 3375251881Speter svn_wc_conflict_reason_unversioned, 3376251881Speter svn_wc_conflict_action_add, 3377362181Sdim NULL, NULL, 3378251881Speter fb->pool, scratch_pool)); 3379251881Speter } 3380251881Speter } 3381251881Speter 3382251881Speter /* When this is not the update target add a not-present BASE node now, 3383251881Speter to allow marking the parent directory complete in its close_edit() call. 3384251881Speter This resolves issues when that occurs before the close_file(). */ 3385251881Speter if (pb->parent_baton 3386251881Speter || *eb->target_basename == '\0' 3387251881Speter || (strcmp(fb->local_abspath, eb->target_abspath) != 0)) 3388251881Speter { 3389289180Speter svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name), 3390289180Speter svn_node_kind_to_word(svn_node_file)); 3391251881Speter } 3392251881Speter 3393251881Speter if (tree_conflict != NULL) 3394251881Speter { 3395251881Speter SVN_ERR(complete_conflict(tree_conflict, 3396251881Speter fb->edit_baton, 3397251881Speter fb->local_abspath, 3398251881Speter fb->old_repos_relpath, 3399251881Speter fb->old_revision, 3400289180Speter fb->new_repos_relpath, 3401289180Speter wc_kind, svn_node_file, 3402289180Speter pb->deletion_conflicts 3403289180Speter ? svn_hash_gets(pb->deletion_conflicts, 3404289180Speter fb->name) 3405289180Speter : NULL, 3406251881Speter fb->pool, scratch_pool)); 3407251881Speter 3408251881Speter SVN_ERR(svn_wc__db_op_mark_conflict(eb->db, 3409251881Speter fb->local_abspath, 3410251881Speter tree_conflict, NULL, 3411251881Speter scratch_pool)); 3412251881Speter 3413289180Speter fb->edit_conflict = tree_conflict; 3414253734Speter 3415251881Speter fb->already_notified = TRUE; 3416251881Speter do_notification(eb, fb->local_abspath, svn_node_file, 3417251881Speter svn_wc_notify_tree_conflict, scratch_pool); 3418251881Speter } 3419251881Speter 3420251881Speter svn_pool_destroy(scratch_pool); 3421251881Speter 3422251881Speter return SVN_NO_ERROR; 3423251881Speter} 3424251881Speter 3425251881Speter 3426251881Speter/* An svn_delta_editor_t function. */ 3427251881Speterstatic svn_error_t * 3428251881Speteropen_file(const char *path, 3429251881Speter void *parent_baton, 3430251881Speter svn_revnum_t base_revision, 3431251881Speter apr_pool_t *pool, 3432251881Speter void **file_baton) 3433251881Speter{ 3434251881Speter struct dir_baton *pb = parent_baton; 3435251881Speter struct edit_baton *eb = pb->edit_baton; 3436251881Speter struct file_baton *fb; 3437251881Speter svn_boolean_t conflicted; 3438251881Speter svn_boolean_t conflict_ignored = FALSE; 3439251881Speter svn_boolean_t have_work; 3440251881Speter svn_wc__db_status_t status; 3441251881Speter svn_node_kind_t wc_kind; 3442251881Speter svn_skel_t *tree_conflict = NULL; 3443251881Speter 3444251881Speter /* the file_pool can stick around for a *long* time, so we want to use 3445251881Speter a subpool for any temporary allocations. */ 3446251881Speter apr_pool_t *scratch_pool = svn_pool_create(pool); 3447251881Speter 3448251881Speter SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool)); 3449251881Speter *file_baton = fb; 3450251881Speter 3451251881Speter if (fb->skip_this) 3452251881Speter return SVN_NO_ERROR; 3453251881Speter 3454251881Speter /* Detect obstructing working copies */ 3455251881Speter { 3456251881Speter svn_boolean_t is_root; 3457251881Speter 3458251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath, 3459251881Speter pool)); 3460251881Speter 3461251881Speter if (is_root) 3462251881Speter { 3463251881Speter /* Just skip this node; a future update will handle it */ 3464251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3465251881Speter fb->skip_this = TRUE; 3466251881Speter fb->already_notified = TRUE; 3467251881Speter 3468251881Speter do_notification(eb, fb->local_abspath, svn_node_file, 3469251881Speter svn_wc_notify_update_skip_obstruction, pool); 3470251881Speter 3471251881Speter return SVN_NO_ERROR; 3472251881Speter } 3473251881Speter } 3474251881Speter 3475251881Speter /* Sanity check. */ 3476251881Speter 3477251881Speter SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision, 3478251881Speter &fb->old_repos_relpath, NULL, NULL, 3479251881Speter &fb->changed_rev, &fb->changed_date, 3480251881Speter &fb->changed_author, NULL, 3481251881Speter &fb->original_checksum, NULL, NULL, NULL, 3482251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 3483251881Speter &conflicted, NULL, NULL, &fb->local_prop_mods, 3484251881Speter NULL, NULL, &have_work, 3485251881Speter eb->db, fb->local_abspath, 3486251881Speter fb->pool, scratch_pool)); 3487251881Speter 3488251881Speter if (have_work) 3489251881Speter SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision, 3490251881Speter &fb->old_repos_relpath, NULL, NULL, 3491251881Speter &fb->changed_rev, &fb->changed_date, 3492251881Speter &fb->changed_author, NULL, 3493251881Speter &fb->original_checksum, NULL, NULL, 3494251881Speter NULL, NULL, NULL, 3495251881Speter eb->db, fb->local_abspath, 3496251881Speter fb->pool, scratch_pool)); 3497251881Speter 3498289180Speter SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath, 3499289180Speter fb->old_repos_relpath, eb, pb, 3500289180Speter fb->pool, scratch_pool)); 3501289180Speter 3502251881Speter /* Is this path a conflict victim? */ 3503251881Speter if (fb->shadowed) 3504251881Speter conflicted = FALSE; /* Conflict applies to WORKING */ 3505251881Speter else if (conflicted) 3506251881Speter SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored, 3507251881Speter eb->db, fb->local_abspath, pool)); 3508251881Speter if (conflicted) 3509251881Speter { 3510251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool)); 3511251881Speter 3512251881Speter fb->skip_this = TRUE; 3513251881Speter fb->already_notified = TRUE; 3514251881Speter 3515251881Speter do_notification(eb, fb->local_abspath, svn_node_unknown, 3516251881Speter svn_wc_notify_skip_conflicted, scratch_pool); 3517251881Speter 3518251881Speter svn_pool_destroy(scratch_pool); 3519251881Speter 3520251881Speter return SVN_NO_ERROR; 3521251881Speter } 3522251881Speter else if (conflict_ignored) 3523251881Speter { 3524251881Speter fb->shadowed = TRUE; 3525251881Speter } 3526251881Speter 3527251881Speter /* Check for conflicts only when we haven't already recorded 3528251881Speter * a tree-conflict on a parent node. */ 3529251881Speter if (!fb->shadowed) 3530251881Speter SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath, 3531251881Speter status, TRUE, svn_node_file, 3532251881Speter svn_wc_conflict_action_edit, 3533251881Speter fb->pool, scratch_pool)); 3534251881Speter 3535251881Speter /* Is this path the victim of a newly-discovered tree conflict? */ 3536251881Speter if (tree_conflict != NULL) 3537251881Speter { 3538251881Speter svn_wc_conflict_reason_t reason; 3539251881Speter fb->edit_conflict = tree_conflict; 3540251881Speter /* Other modifications wouldn't be a tree conflict */ 3541251881Speter 3542362181Sdim SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, NULL, 3543251881Speter eb->db, fb->local_abspath, 3544251881Speter tree_conflict, 3545251881Speter scratch_pool, scratch_pool)); 3546251881Speter SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted 3547251881Speter || reason == svn_wc_conflict_reason_moved_away 3548251881Speter || reason == svn_wc_conflict_reason_replaced 3549251881Speter || reason == svn_wc_conflict_reason_obstructed); 3550251881Speter 3551251881Speter /* Continue updating BASE */ 3552251881Speter if (reason == svn_wc_conflict_reason_obstructed) 3553251881Speter fb->edit_obstructed = TRUE; 3554251881Speter else 3555251881Speter fb->shadowed = TRUE; 3556251881Speter } 3557251881Speter 3558251881Speter svn_pool_destroy(scratch_pool); 3559251881Speter 3560251881Speter return SVN_NO_ERROR; 3561251881Speter} 3562251881Speter 3563251881Speter/* Implements svn_stream_lazyopen_func_t. */ 3564251881Speterstatic svn_error_t * 3565251881Speterlazy_open_source(svn_stream_t **stream, 3566251881Speter void *baton, 3567251881Speter apr_pool_t *result_pool, 3568251881Speter apr_pool_t *scratch_pool) 3569251881Speter{ 3570251881Speter struct file_baton *fb = baton; 3571251881Speter 3572251881Speter SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db, 3573251881Speter fb->local_abspath, 3574251881Speter fb->original_checksum, 3575251881Speter result_pool, scratch_pool)); 3576251881Speter 3577251881Speter 3578251881Speter return SVN_NO_ERROR; 3579251881Speter} 3580251881Speter 3581251881Speter/* Implements svn_stream_lazyopen_func_t. */ 3582251881Speterstatic svn_error_t * 3583251881Speterlazy_open_target(svn_stream_t **stream, 3584251881Speter void *baton, 3585251881Speter apr_pool_t *result_pool, 3586251881Speter apr_pool_t *scratch_pool) 3587251881Speter{ 3588289180Speter struct handler_baton *hb = baton; 3589289180Speter svn_wc__db_install_data_t *install_data; 3590251881Speter 3591289180Speter /* By convention return value is undefined on error, but we rely 3592289180Speter on HB->INSTALL_DATA value in window_handler() and abort 3593289180Speter INSTALL_STREAM if is not NULL on error. 3594289180Speter So we store INSTALL_DATA to local variable first, to leave 3595289180Speter HB->INSTALL_DATA unchanged on error. */ 3596289180Speter SVN_ERR(svn_wc__db_pristine_prepare_install(stream, 3597289180Speter &install_data, 3598289180Speter &hb->new_text_base_sha1_checksum, 3599289180Speter NULL, 3600289180Speter hb->fb->edit_baton->db, 3601289180Speter hb->fb->dir_baton->local_abspath, 3602289180Speter result_pool, scratch_pool)); 3603251881Speter 3604289180Speter hb->install_data = install_data; 3605289180Speter 3606251881Speter return SVN_NO_ERROR; 3607251881Speter} 3608251881Speter 3609251881Speter/* An svn_delta_editor_t function. */ 3610251881Speterstatic svn_error_t * 3611251881Speterapply_textdelta(void *file_baton, 3612251881Speter const char *expected_checksum, 3613251881Speter apr_pool_t *pool, 3614251881Speter svn_txdelta_window_handler_t *handler, 3615251881Speter void **handler_baton) 3616251881Speter{ 3617251881Speter struct file_baton *fb = file_baton; 3618251881Speter apr_pool_t *handler_pool = svn_pool_create(fb->pool); 3619251881Speter struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb)); 3620251881Speter struct edit_baton *eb = fb->edit_baton; 3621251881Speter const svn_checksum_t *recorded_base_checksum; 3622251881Speter svn_checksum_t *expected_base_checksum; 3623251881Speter svn_stream_t *source; 3624251881Speter svn_stream_t *target; 3625251881Speter 3626251881Speter if (fb->skip_this) 3627251881Speter { 3628251881Speter *handler = svn_delta_noop_window_handler; 3629251881Speter *handler_baton = NULL; 3630251881Speter return SVN_NO_ERROR; 3631251881Speter } 3632251881Speter 3633251881Speter SVN_ERR(mark_file_edited(fb, pool)); 3634251881Speter 3635251881Speter /* Parse checksum or sets expected_base_checksum to NULL */ 3636251881Speter SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5, 3637251881Speter expected_checksum, pool)); 3638251881Speter 3639251881Speter /* Before applying incoming svndiff data to text base, make sure 3640251881Speter text base hasn't been corrupted, and that its checksum 3641251881Speter matches the expected base checksum. */ 3642251881Speter 3643251881Speter /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and 3644251881Speter check our RECORDED_BASE_CHECKSUM. (In WC-1, we could not do this test 3645251881Speter for replaced nodes because we didn't store the checksum of the "revert 3646251881Speter base". In WC-NG, we do and we can.) */ 3647251881Speter recorded_base_checksum = fb->original_checksum; 3648251881Speter 3649251881Speter /* If we have a checksum that we want to compare to a MD5 checksum, 3650251881Speter ensure that it is a MD5 checksum */ 3651251881Speter if (recorded_base_checksum 3652251881Speter && expected_base_checksum 3653251881Speter && recorded_base_checksum->kind != svn_checksum_md5) 3654251881Speter SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum, 3655251881Speter eb->db, eb->wcroot_abspath, 3656251881Speter recorded_base_checksum, pool, pool)); 3657251881Speter 3658251881Speter 3659251881Speter if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum)) 3660251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL, 3661251881Speter _("Checksum mismatch for '%s':\n" 3662251881Speter " expected: %s\n" 3663251881Speter " recorded: %s\n"), 3664251881Speter svn_dirent_local_style(fb->local_abspath, pool), 3665251881Speter svn_checksum_to_cstring_display(expected_base_checksum, 3666251881Speter pool), 3667251881Speter svn_checksum_to_cstring_display(recorded_base_checksum, 3668251881Speter pool)); 3669251881Speter 3670251881Speter /* Open the text base for reading, unless this is an added file. */ 3671251881Speter 3672251881Speter /* 3673251881Speter kff todo: what we really need to do here is: 3674251881Speter 3675251881Speter 1. See if there's a file or dir by this name already here. 3676251881Speter 2. See if it's under revision control. 3677251881Speter 3. If both are true, open text-base. 3678251881Speter 4. If only 1 is true, bail, because we can't go destroying user's 3679251881Speter files (or as an alternative to bailing, move it to some tmp 3680251881Speter name and somehow tell the user, but communicating with the 3681251881Speter user without erroring is a whole callback system we haven't 3682251881Speter finished inventing yet.) 3683251881Speter */ 3684251881Speter 3685251881Speter if (! fb->adding_file) 3686251881Speter { 3687251881Speter SVN_ERR_ASSERT(!fb->original_checksum 3688251881Speter || fb->original_checksum->kind == svn_checksum_sha1); 3689251881Speter 3690251881Speter source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE, 3691251881Speter handler_pool); 3692251881Speter } 3693251881Speter else 3694251881Speter { 3695251881Speter source = svn_stream_empty(handler_pool); 3696251881Speter } 3697251881Speter 3698251881Speter /* If we don't have a recorded checksum, use the ra provided checksum */ 3699251881Speter if (!recorded_base_checksum) 3700251881Speter recorded_base_checksum = expected_base_checksum; 3701251881Speter 3702251881Speter /* Checksum the text base while applying deltas */ 3703251881Speter if (recorded_base_checksum) 3704251881Speter { 3705251881Speter hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum, 3706251881Speter handler_pool); 3707251881Speter 3708251881Speter /* Wrap stream and store reference to allow calculating the 3709251881Speter checksum. */ 3710251881Speter source = svn_stream_checksummed2(source, 3711251881Speter &hb->actual_source_checksum, 3712251881Speter NULL, recorded_base_checksum->kind, 3713251881Speter TRUE, handler_pool); 3714251881Speter hb->source_checksum_stream = source; 3715251881Speter } 3716251881Speter 3717289180Speter target = svn_stream_lazyopen_create(lazy_open_target, hb, TRUE, handler_pool); 3718251881Speter 3719251881Speter /* Prepare to apply the delta. */ 3720251881Speter svn_txdelta_apply(source, target, 3721251881Speter hb->new_text_base_md5_digest, 3722289180Speter fb->local_abspath /* error_info */, 3723251881Speter handler_pool, 3724251881Speter &hb->apply_handler, &hb->apply_baton); 3725251881Speter 3726251881Speter hb->pool = handler_pool; 3727251881Speter hb->fb = fb; 3728251881Speter 3729251881Speter /* We're all set. */ 3730251881Speter *handler_baton = hb; 3731251881Speter *handler = window_handler; 3732251881Speter 3733251881Speter return SVN_NO_ERROR; 3734251881Speter} 3735251881Speter 3736251881Speter 3737251881Speter/* An svn_delta_editor_t function. */ 3738251881Speterstatic svn_error_t * 3739251881Speterchange_file_prop(void *file_baton, 3740251881Speter const char *name, 3741251881Speter const svn_string_t *value, 3742251881Speter apr_pool_t *scratch_pool) 3743251881Speter{ 3744251881Speter struct file_baton *fb = file_baton; 3745251881Speter svn_prop_t *propchange; 3746251881Speter 3747251881Speter if (fb->skip_this) 3748251881Speter return SVN_NO_ERROR; 3749251881Speter 3750251881Speter /* Push a new propchange to the file baton's array of propchanges */ 3751251881Speter propchange = apr_array_push(fb->propchanges); 3752251881Speter propchange->name = apr_pstrdup(fb->pool, name); 3753289180Speter propchange->value = svn_string_dup(value, fb->pool); 3754251881Speter 3755251881Speter if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind) 3756251881Speter SVN_ERR(mark_file_edited(fb, scratch_pool)); 3757251881Speter 3758251881Speter if (! fb->shadowed 3759251881Speter && strcmp(name, SVN_PROP_SPECIAL) == 0) 3760251881Speter { 3761251881Speter struct edit_baton *eb = fb->edit_baton; 3762251881Speter svn_boolean_t modified = FALSE; 3763251881Speter svn_boolean_t becomes_symlink; 3764251881Speter svn_boolean_t was_symlink; 3765251881Speter 3766251881Speter /* Let's see if we have a change as in some scenarios servers report 3767251881Speter non-changes of properties. */ 3768251881Speter becomes_symlink = (value != NULL); 3769251881Speter 3770251881Speter if (fb->adding_file) 3771251881Speter was_symlink = becomes_symlink; /* No change */ 3772251881Speter else 3773251881Speter { 3774251881Speter apr_hash_t *props; 3775251881Speter 3776251881Speter /* We read the server-props, not the ACTUAL props here as we just 3777251881Speter want to see if this is really an incoming prop change. */ 3778251881Speter SVN_ERR(svn_wc__db_base_get_props(&props, eb->db, 3779251881Speter fb->local_abspath, 3780251881Speter scratch_pool, scratch_pool)); 3781251881Speter 3782251881Speter was_symlink = ((props 3783251881Speter && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL) 3784251881Speter ? svn_tristate_true 3785251881Speter : svn_tristate_false); 3786251881Speter } 3787251881Speter 3788251881Speter if (was_symlink != becomes_symlink) 3789251881Speter { 3790251881Speter /* If the local node was not modified, we continue as usual, if 3791251881Speter modified we want a tree conflict just like how we would handle 3792251881Speter it when receiving a delete + add (aka "replace") */ 3793251881Speter if (fb->local_prop_mods) 3794251881Speter modified = TRUE; 3795251881Speter else 3796251881Speter SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db, 3797251881Speter fb->local_abspath, 3798251881Speter FALSE, scratch_pool)); 3799251881Speter } 3800251881Speter 3801251881Speter if (modified) 3802251881Speter { 3803251881Speter if (!fb->edit_conflict) 3804251881Speter fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool); 3805251881Speter 3806251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 3807251881Speter fb->edit_conflict, 3808251881Speter eb->db, fb->local_abspath, 3809251881Speter svn_wc_conflict_reason_edited, 3810251881Speter svn_wc_conflict_action_replace, 3811362181Sdim NULL, NULL, 3812251881Speter fb->pool, scratch_pool)); 3813251881Speter 3814251881Speter SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton, 3815251881Speter fb->local_abspath, fb->old_repos_relpath, 3816289180Speter fb->old_revision, fb->new_repos_relpath, 3817251881Speter svn_node_file, svn_node_file, 3818289180Speter NULL, fb->pool, scratch_pool)); 3819251881Speter 3820251881Speter /* Create a copy of the existing (pre update) BASE node in WORKING, 3821251881Speter mark a tree conflict and handle the rest of the update as 3822251881Speter shadowed */ 3823251881Speter SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath, 3824251881Speter fb->edit_conflict, NULL, 3825251881Speter scratch_pool)); 3826251881Speter 3827251881Speter do_notification(eb, fb->local_abspath, svn_node_file, 3828251881Speter svn_wc_notify_tree_conflict, scratch_pool); 3829251881Speter 3830251881Speter /* Ok, we introduced a replacement, so we can now handle the rest 3831251881Speter as a normal shadowed update */ 3832251881Speter fb->shadowed = TRUE; 3833251881Speter fb->add_existed = FALSE; 3834251881Speter fb->already_notified = TRUE; 3835251881Speter } 3836251881Speter } 3837251881Speter 3838251881Speter return SVN_NO_ERROR; 3839251881Speter} 3840251881Speter 3841251881Speter/* Perform the actual merge of file changes between an original file, 3842251881Speter identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file 3843251881Speter identified by NEW_CHECKSUM. 3844251881Speter 3845251881Speter Merge the result into LOCAL_ABSPATH, which is part of the working copy 3846251881Speter identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming 3847251881Speter the intermediate files. 3848251881Speter 3849251881Speter The rest of the arguments are passed to svn_wc__internal_merge(). 3850251881Speter */ 3851251881Spetersvn_error_t * 3852251881Spetersvn_wc__perform_file_merge(svn_skel_t **work_items, 3853251881Speter svn_skel_t **conflict_skel, 3854251881Speter svn_boolean_t *found_conflict, 3855251881Speter svn_wc__db_t *db, 3856251881Speter const char *local_abspath, 3857251881Speter const char *wri_abspath, 3858251881Speter const svn_checksum_t *new_checksum, 3859251881Speter const svn_checksum_t *original_checksum, 3860251881Speter apr_hash_t *old_actual_props, 3861251881Speter const apr_array_header_t *ext_patterns, 3862251881Speter svn_revnum_t old_revision, 3863251881Speter svn_revnum_t target_revision, 3864251881Speter const apr_array_header_t *propchanges, 3865251881Speter const char *diff3_cmd, 3866251881Speter svn_cancel_func_t cancel_func, 3867251881Speter void *cancel_baton, 3868251881Speter apr_pool_t *result_pool, 3869251881Speter apr_pool_t *scratch_pool) 3870251881Speter{ 3871251881Speter /* Actual file exists and has local mods: 3872251881Speter Now we need to let loose svn_wc__internal_merge() to merge 3873251881Speter the textual changes into the working file. */ 3874251881Speter const char *oldrev_str, *newrev_str, *mine_str; 3875251881Speter const char *merge_left; 3876251881Speter svn_boolean_t delete_left = FALSE; 3877251881Speter const char *path_ext = ""; 3878289180Speter const char *new_pristine_abspath; 3879251881Speter enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged; 3880251881Speter svn_skel_t *work_item; 3881251881Speter 3882251881Speter *work_items = NULL; 3883251881Speter 3884289180Speter SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, 3885251881Speter db, wri_abspath, new_checksum, 3886251881Speter scratch_pool, scratch_pool)); 3887251881Speter 3888251881Speter /* If we have any file extensions we're supposed to 3889251881Speter preserve in generated conflict file names, then find 3890251881Speter this path's extension. But then, if it isn't one of 3891251881Speter the ones we want to keep in conflict filenames, 3892251881Speter pretend it doesn't have an extension at all. */ 3893251881Speter if (ext_patterns && ext_patterns->nelts) 3894251881Speter { 3895251881Speter svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool); 3896251881Speter if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns))) 3897251881Speter path_ext = ""; 3898251881Speter } 3899251881Speter 3900251881Speter /* old_revision can be invalid when the conflict is against a 3901251881Speter local addition */ 3902251881Speter if (!SVN_IS_VALID_REVNUM(old_revision)) 3903251881Speter old_revision = 0; 3904251881Speter 3905251881Speter oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s", 3906251881Speter old_revision, 3907251881Speter *path_ext ? "." : "", 3908251881Speter *path_ext ? path_ext : ""); 3909251881Speter 3910251881Speter newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s", 3911251881Speter target_revision, 3912251881Speter *path_ext ? "." : "", 3913251881Speter *path_ext ? path_ext : ""); 3914251881Speter mine_str = apr_psprintf(scratch_pool, ".mine%s%s", 3915251881Speter *path_ext ? "." : "", 3916251881Speter *path_ext ? path_ext : ""); 3917251881Speter 3918251881Speter if (! original_checksum) 3919251881Speter { 3920251881Speter SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath, 3921251881Speter result_pool, scratch_pool)); 3922251881Speter delete_left = TRUE; 3923251881Speter } 3924251881Speter else 3925251881Speter SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath, 3926251881Speter original_checksum, 3927251881Speter result_pool, scratch_pool)); 3928251881Speter 3929251881Speter /* Merge the changes from the old textbase to the new 3930251881Speter textbase into the file we're updating. 3931251881Speter Remember that this function wants full paths! */ 3932251881Speter SVN_ERR(svn_wc__internal_merge(&work_item, 3933251881Speter conflict_skel, 3934251881Speter &merge_outcome, 3935251881Speter db, 3936251881Speter merge_left, 3937289180Speter new_pristine_abspath, 3938251881Speter local_abspath, 3939251881Speter wri_abspath, 3940251881Speter oldrev_str, newrev_str, mine_str, 3941251881Speter old_actual_props, 3942251881Speter FALSE /* dry_run */, 3943251881Speter diff3_cmd, NULL, propchanges, 3944251881Speter cancel_func, cancel_baton, 3945251881Speter result_pool, scratch_pool)); 3946251881Speter 3947251881Speter *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 3948251881Speter *found_conflict = (merge_outcome == svn_wc_merge_conflict); 3949251881Speter 3950251881Speter /* If we created a temporary left merge file, get rid of it. */ 3951251881Speter if (delete_left) 3952251881Speter { 3953251881Speter SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath, 3954251881Speter merge_left, 3955251881Speter result_pool, scratch_pool)); 3956251881Speter *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool); 3957251881Speter } 3958251881Speter 3959251881Speter return SVN_NO_ERROR; 3960251881Speter} 3961251881Speter 3962251881Speter/* This is the small planet. It has the complex responsibility of 3963251881Speter * "integrating" a new revision of a file into a working copy. 3964251881Speter * 3965251881Speter * Given a file_baton FB for a file either already under version control, or 3966251881Speter * prepared (see below) to join version control, fully install a 3967251881Speter * new revision of the file. 3968251881Speter * 3969251881Speter * ### transitional: installation of the working file will be handled 3970251881Speter * ### by the *INSTALL_PRISTINE flag. 3971251881Speter * 3972251881Speter * By "install", we mean: create a new text-base and prop-base, merge 3973251881Speter * any textual and property changes into the working file, and finally 3974251881Speter * update all metadata so that the working copy believes it has a new 3975251881Speter * working revision of the file. All of this work includes being 3976251881Speter * sensitive to eol translation, keyword substitution, and performing 3977251881Speter * all actions accumulated the parent directory's work queue. 3978251881Speter * 3979251881Speter * Set *CONTENT_STATE to the state of the contents after the 3980251881Speter * installation. 3981251881Speter * 3982251881Speter * Return values are allocated in RESULT_POOL and temporary allocations 3983251881Speter * are performed in SCRATCH_POOL. 3984251881Speter */ 3985251881Speterstatic svn_error_t * 3986251881Spetermerge_file(svn_skel_t **work_items, 3987251881Speter svn_skel_t **conflict_skel, 3988251881Speter svn_boolean_t *install_pristine, 3989251881Speter const char **install_from, 3990251881Speter svn_wc_notify_state_t *content_state, 3991251881Speter struct file_baton *fb, 3992251881Speter apr_hash_t *actual_props, 3993251881Speter apr_time_t last_changed_date, 3994251881Speter apr_pool_t *result_pool, 3995251881Speter apr_pool_t *scratch_pool) 3996251881Speter{ 3997251881Speter struct edit_baton *eb = fb->edit_baton; 3998251881Speter struct dir_baton *pb = fb->dir_baton; 3999251881Speter svn_boolean_t is_locally_modified; 4000251881Speter svn_boolean_t found_text_conflict = FALSE; 4001251881Speter 4002251881Speter SVN_ERR_ASSERT(! fb->shadowed 4003251881Speter && ! fb->obstruction_found 4004251881Speter && ! fb->edit_obstructed); 4005251881Speter 4006251881Speter /* 4007251881Speter When this function is called on file F, we assume the following 4008251881Speter things are true: 4009251881Speter 4010251881Speter - The new pristine text of F is present in the pristine store 4011251881Speter iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL. 4012251881Speter 4013251881Speter - The WC metadata still reflects the old version of F. 4014251881Speter (We can still access the old pristine base text of F.) 4015251881Speter 4016251881Speter The goal is to update the local working copy of F to reflect 4017251881Speter the changes received from the repository, preserving any local 4018251881Speter modifications. 4019251881Speter */ 4020251881Speter 4021251881Speter *work_items = NULL; 4022251881Speter *install_pristine = FALSE; 4023251881Speter *install_from = NULL; 4024251881Speter 4025251881Speter /* Start by splitting the file path, getting an access baton for the parent, 4026251881Speter and an entry for the file if any. */ 4027251881Speter 4028251881Speter /* Has the user made local mods to the working file? 4029251881Speter Note that this compares to the current pristine file, which is 4030251881Speter different from fb->old_text_base_path if we have a replaced-with-history 4031251881Speter file. However, in the case we had an obstruction, we check against the 4032251881Speter new text base. 4033251881Speter */ 4034251881Speter if (fb->adding_file && !fb->add_existed) 4035251881Speter { 4036251881Speter is_locally_modified = FALSE; /* There is no file: Don't check */ 4037251881Speter } 4038251881Speter else 4039251881Speter { 4040251881Speter /* The working file is not an obstruction. 4041251881Speter So: is the file modified, relative to its ORIGINAL pristine? 4042251881Speter 4043251881Speter This function sets is_locally_modified to FALSE for 4044251881Speter files that do not exist and for directories. */ 4045251881Speter 4046251881Speter SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified, 4047251881Speter eb->db, fb->local_abspath, 4048251881Speter FALSE /* exact_comparison */, 4049251881Speter scratch_pool)); 4050251881Speter } 4051251881Speter 4052251881Speter /* For 'textual' merging, we use the following system: 4053251881Speter 4054251881Speter When a file is modified and we have a new BASE: 4055251881Speter - For text files 4056251881Speter * svn_wc_merge uses diff3 4057251881Speter * possibly makes backups and marks files as conflicted. 4058251881Speter 4059251881Speter - For binary files 4060251881Speter * svn_wc_merge makes backups and marks files as conflicted. 4061251881Speter 4062251881Speter If a file is not modified and we have a new BASE: 4063251881Speter * Install from pristine. 4064251881Speter 4065251881Speter If we have property changes related to magic properties or if the 4066251881Speter svn:keywords property is set: 4067251881Speter * Retranslate from the working file. 4068251881Speter */ 4069251881Speter if (! is_locally_modified 4070251881Speter && fb->new_text_base_sha1_checksum) 4071251881Speter { 4072251881Speter /* If there are no local mods, who cares whether it's a text 4073251881Speter or binary file! Just write a log command to overwrite 4074251881Speter any working file with the new text-base. If newline 4075251881Speter conversion or keyword substitution is activated, this 4076251881Speter will happen as well during the copy. 4077251881Speter For replaced files, though, we want to merge in the changes 4078251881Speter even if the file is not modified compared to the (non-revert) 4079251881Speter text-base. */ 4080251881Speter 4081251881Speter *install_pristine = TRUE; 4082251881Speter } 4083251881Speter else if (fb->new_text_base_sha1_checksum) 4084251881Speter { 4085251881Speter /* Actual file exists and has local mods: 4086251881Speter Now we need to let loose svn_wc__merge_internal() to merge 4087251881Speter the textual changes into the working file. */ 4088251881Speter SVN_ERR(svn_wc__perform_file_merge(work_items, 4089251881Speter conflict_skel, 4090251881Speter &found_text_conflict, 4091251881Speter eb->db, 4092251881Speter fb->local_abspath, 4093251881Speter pb->local_abspath, 4094251881Speter fb->new_text_base_sha1_checksum, 4095251881Speter fb->add_existed 4096251881Speter ? NULL 4097251881Speter : fb->original_checksum, 4098251881Speter actual_props, 4099251881Speter eb->ext_patterns, 4100251881Speter fb->old_revision, 4101251881Speter *eb->target_revision, 4102251881Speter fb->propchanges, 4103251881Speter eb->diff3_cmd, 4104251881Speter eb->cancel_func, eb->cancel_baton, 4105251881Speter result_pool, scratch_pool)); 4106251881Speter } /* end: working file exists and has mods */ 4107251881Speter else 4108251881Speter { 4109251881Speter /* There is no new text base, but let's see if the working file needs 4110251881Speter to be updated for any other reason. */ 4111251881Speter 4112251881Speter apr_hash_t *keywords; 4113251881Speter 4114251881Speter /* Determine if any of the propchanges are the "magic" ones that 4115251881Speter might require changing the working file. */ 4116251881Speter svn_boolean_t magic_props_changed; 4117251881Speter 4118251881Speter magic_props_changed = svn_wc__has_magic_property(fb->propchanges); 4119251881Speter 4120251881Speter SVN_ERR(svn_wc__get_translate_info(NULL, NULL, 4121251881Speter &keywords, 4122251881Speter NULL, 4123251881Speter eb->db, fb->local_abspath, 4124251881Speter actual_props, TRUE, 4125251881Speter scratch_pool, scratch_pool)); 4126251881Speter if (magic_props_changed || keywords) 4127251881Speter { 4128251881Speter /* Special edge-case: it's possible that this file installation 4129251881Speter only involves propchanges, but that some of those props still 4130251881Speter require a retranslation of the working file. 4131251881Speter 4132251881Speter OR that the file doesn't involve propchanges which by themselves 4133251881Speter require retranslation, but receiving a change bumps the revision 4134251881Speter number which requires re-expansion of keywords... */ 4135251881Speter 4136251881Speter if (is_locally_modified) 4137251881Speter { 4138251881Speter const char *tmptext; 4139251881Speter 4140251881Speter /* Copy and DEtranslate the working file to a temp text-base. 4141251881Speter Note that detranslation is done according to the old props. */ 4142251881Speter SVN_ERR(svn_wc__internal_translated_file( 4143251881Speter &tmptext, fb->local_abspath, eb->db, fb->local_abspath, 4144251881Speter SVN_WC_TRANSLATE_TO_NF 4145251881Speter | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP, 4146251881Speter eb->cancel_func, eb->cancel_baton, 4147251881Speter result_pool, scratch_pool)); 4148251881Speter 4149251881Speter /* We always want to reinstall the working file if the magic 4150251881Speter properties have changed, or there are any keywords present. 4151251881Speter Note that TMPTEXT might actually refer to the working file 4152251881Speter itself (the above function skips a detranslate when not 4153251881Speter required). This is acceptable, as we will (re)translate 4154251881Speter according to the new properties into a temporary file (from 4155251881Speter the working file), and then rename the temp into place. Magic! 4156251881Speter */ 4157251881Speter *install_pristine = TRUE; 4158251881Speter *install_from = tmptext; 4159251881Speter } 4160251881Speter else 4161251881Speter { 4162251881Speter /* Use our existing 'copy' from the pristine store instead 4163251881Speter of making a new copy. This way we can use the standard code 4164251881Speter to update the recorded size and modification time. 4165251881Speter (Issue #3842) */ 4166251881Speter *install_pristine = TRUE; 4167251881Speter } 4168251881Speter } 4169251881Speter } 4170251881Speter 4171251881Speter /* Set the returned content state. */ 4172251881Speter 4173251881Speter if (found_text_conflict) 4174251881Speter *content_state = svn_wc_notify_state_conflicted; 4175251881Speter else if (fb->new_text_base_sha1_checksum) 4176251881Speter { 4177251881Speter if (is_locally_modified) 4178251881Speter *content_state = svn_wc_notify_state_merged; 4179251881Speter else 4180251881Speter *content_state = svn_wc_notify_state_changed; 4181251881Speter } 4182251881Speter else 4183251881Speter *content_state = svn_wc_notify_state_unchanged; 4184251881Speter 4185251881Speter return SVN_NO_ERROR; 4186251881Speter} 4187251881Speter 4188251881Speter 4189251881Speter/* An svn_delta_editor_t function. */ 4190251881Speter/* Mostly a wrapper around merge_file. */ 4191251881Speterstatic svn_error_t * 4192251881Speterclose_file(void *file_baton, 4193251881Speter const char *expected_md5_digest, 4194251881Speter apr_pool_t *pool) 4195251881Speter{ 4196251881Speter struct file_baton *fb = file_baton; 4197251881Speter struct dir_baton *pdb = fb->dir_baton; 4198251881Speter struct edit_baton *eb = fb->edit_baton; 4199251881Speter svn_wc_notify_state_t content_state, prop_state; 4200251881Speter svn_wc_notify_lock_state_t lock_state; 4201251881Speter svn_checksum_t *expected_md5_checksum = NULL; 4202251881Speter apr_hash_t *new_base_props = NULL; 4203251881Speter apr_hash_t *new_actual_props = NULL; 4204251881Speter apr_array_header_t *entry_prop_changes; 4205251881Speter apr_array_header_t *dav_prop_changes; 4206251881Speter apr_array_header_t *regular_prop_changes; 4207251881Speter apr_hash_t *current_base_props = NULL; 4208251881Speter apr_hash_t *current_actual_props = NULL; 4209251881Speter apr_hash_t *local_actual_props = NULL; 4210251881Speter svn_skel_t *all_work_items = NULL; 4211251881Speter svn_skel_t *conflict_skel = NULL; 4212251881Speter svn_skel_t *work_item; 4213251881Speter apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */ 4214251881Speter svn_boolean_t keep_recorded_info = FALSE; 4215251881Speter const svn_checksum_t *new_checksum; 4216251881Speter apr_array_header_t *iprops = NULL; 4217251881Speter 4218251881Speter if (fb->skip_this) 4219251881Speter { 4220251881Speter svn_pool_destroy(fb->pool); 4221251881Speter SVN_ERR(maybe_release_dir_info(pdb)); 4222251881Speter return SVN_NO_ERROR; 4223251881Speter } 4224251881Speter 4225251881Speter if (fb->edited) 4226251881Speter conflict_skel = fb->edit_conflict; 4227251881Speter 4228251881Speter if (expected_md5_digest) 4229251881Speter SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5, 4230251881Speter expected_md5_digest, scratch_pool)); 4231251881Speter 4232251881Speter if (fb->new_text_base_md5_checksum && expected_md5_checksum 4233251881Speter && !svn_checksum_match(expected_md5_checksum, 4234251881Speter fb->new_text_base_md5_checksum)) 4235251881Speter return svn_error_trace( 4236251881Speter svn_checksum_mismatch_err(expected_md5_checksum, 4237251881Speter fb->new_text_base_md5_checksum, 4238251881Speter scratch_pool, 4239251881Speter _("Checksum mismatch for '%s'"), 4240251881Speter svn_dirent_local_style( 4241251881Speter fb->local_abspath, pool))); 4242251881Speter 4243251881Speter /* Gather the changes for each kind of property. */ 4244251881Speter SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes, 4245251881Speter &dav_prop_changes, ®ular_prop_changes, 4246251881Speter scratch_pool)); 4247251881Speter 4248251881Speter /* Extract the changed_* and lock state information. */ 4249251881Speter { 4250251881Speter svn_revnum_t new_changed_rev; 4251251881Speter apr_time_t new_changed_date; 4252251881Speter const char *new_changed_author; 4253251881Speter 4254251881Speter SVN_ERR(accumulate_last_change(&new_changed_rev, 4255251881Speter &new_changed_date, 4256251881Speter &new_changed_author, 4257251881Speter entry_prop_changes, 4258251881Speter scratch_pool, scratch_pool)); 4259251881Speter 4260251881Speter if (SVN_IS_VALID_REVNUM(new_changed_rev)) 4261251881Speter fb->changed_rev = new_changed_rev; 4262251881Speter if (new_changed_date != 0) 4263251881Speter fb->changed_date = new_changed_date; 4264251881Speter if (new_changed_author != NULL) 4265251881Speter fb->changed_author = new_changed_author; 4266251881Speter } 4267251881Speter 4268251881Speter /* Determine whether the file has become unlocked. */ 4269251881Speter { 4270251881Speter int i; 4271251881Speter 4272251881Speter lock_state = svn_wc_notify_lock_state_unchanged; 4273251881Speter 4274251881Speter for (i = 0; i < entry_prop_changes->nelts; ++i) 4275251881Speter { 4276251881Speter const svn_prop_t *prop 4277251881Speter = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t); 4278251881Speter 4279251881Speter /* If we see a change to the LOCK_TOKEN entry prop, then the only 4280251881Speter possible change is its REMOVAL. Thus, the lock has been removed, 4281251881Speter and we should likewise remove our cached copy of it. */ 4282251881Speter if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN)) 4283251881Speter { 4284251881Speter /* If we lose the lock, but not because we are switching to 4285251881Speter another url, remove the state lock from the wc */ 4286289180Speter if (! eb->switch_repos_relpath 4287289180Speter || strcmp(fb->new_repos_relpath, fb->old_repos_relpath) == 0) 4288251881Speter { 4289251881Speter SVN_ERR_ASSERT(prop->value == NULL); 4290289180Speter SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, NULL, 4291251881Speter scratch_pool)); 4292251881Speter 4293251881Speter lock_state = svn_wc_notify_lock_state_unlocked; 4294251881Speter } 4295251881Speter break; 4296251881Speter } 4297251881Speter } 4298251881Speter } 4299251881Speter 4300251881Speter /* Install all kinds of properties. It is important to do this before 4301251881Speter any file content merging, since that process might expand keywords, in 4302251881Speter which case we want the new entryprops to be in place. */ 4303251881Speter 4304251881Speter /* Write log commands to merge REGULAR_PROPS into the existing 4305251881Speter properties of FB->LOCAL_ABSPATH. Update *PROP_STATE to reflect 4306251881Speter the result of the regular prop merge. 4307251881Speter 4308251881Speter BASE_PROPS and WORKING_PROPS are hashes of the base and 4309251881Speter working props of the file; if NULL they are read from the wc. */ 4310251881Speter 4311251881Speter /* ### some of this feels like voodoo... */ 4312251881Speter 4313251881Speter if ((!fb->adding_file || fb->add_existed) 4314251881Speter && !fb->shadowed) 4315251881Speter SVN_ERR(svn_wc__get_actual_props(&local_actual_props, 4316251881Speter eb->db, fb->local_abspath, 4317251881Speter scratch_pool, scratch_pool)); 4318251881Speter if (local_actual_props == NULL) 4319251881Speter local_actual_props = apr_hash_make(scratch_pool); 4320251881Speter 4321251881Speter if (fb->add_existed) 4322251881Speter { 4323251881Speter /* This node already exists. Grab the current pristine properties. */ 4324251881Speter SVN_ERR(svn_wc__db_read_pristine_props(¤t_base_props, 4325251881Speter eb->db, fb->local_abspath, 4326251881Speter scratch_pool, scratch_pool)); 4327251881Speter current_actual_props = local_actual_props; 4328251881Speter } 4329251881Speter else if (!fb->adding_file) 4330251881Speter { 4331251881Speter /* Get the BASE properties for proper merging. */ 4332251881Speter SVN_ERR(svn_wc__db_base_get_props(¤t_base_props, 4333251881Speter eb->db, fb->local_abspath, 4334251881Speter scratch_pool, scratch_pool)); 4335251881Speter current_actual_props = local_actual_props; 4336251881Speter } 4337251881Speter 4338251881Speter /* Note: even if the node existed before, it may not have 4339251881Speter pristine props (e.g a local-add) */ 4340251881Speter if (current_base_props == NULL) 4341251881Speter current_base_props = apr_hash_make(scratch_pool); 4342251881Speter 4343251881Speter /* And new nodes need an empty set of ACTUAL props. */ 4344251881Speter if (current_actual_props == NULL) 4345251881Speter current_actual_props = apr_hash_make(scratch_pool); 4346251881Speter 4347251881Speter prop_state = svn_wc_notify_state_unknown; 4348251881Speter 4349251881Speter if (! fb->shadowed) 4350251881Speter { 4351251881Speter svn_boolean_t install_pristine; 4352251881Speter const char *install_from = NULL; 4353251881Speter 4354251881Speter /* Merge the 'regular' props into the existing working proplist. */ 4355251881Speter /* This will merge the old and new props into a new prop db, and 4356251881Speter write <cp> commands to the logfile to install the merged 4357251881Speter props. */ 4358251881Speter new_base_props = svn_prop__patch(current_base_props, regular_prop_changes, 4359251881Speter scratch_pool); 4360251881Speter SVN_ERR(svn_wc__merge_props(&conflict_skel, 4361251881Speter &prop_state, 4362251881Speter &new_actual_props, 4363251881Speter eb->db, 4364251881Speter fb->local_abspath, 4365251881Speter NULL /* server_baseprops (update, not merge) */, 4366251881Speter current_base_props, 4367251881Speter current_actual_props, 4368251881Speter regular_prop_changes, /* propchanges */ 4369251881Speter scratch_pool, 4370251881Speter scratch_pool)); 4371251881Speter /* We will ALWAYS have properties to save (after a not-dry-run merge). */ 4372251881Speter SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL); 4373251881Speter 4374251881Speter /* Merge the text. This will queue some additional work. */ 4375251881Speter if (!fb->obstruction_found && !fb->edit_obstructed) 4376251881Speter { 4377251881Speter svn_error_t *err; 4378251881Speter err = merge_file(&work_item, &conflict_skel, 4379251881Speter &install_pristine, &install_from, 4380251881Speter &content_state, fb, current_actual_props, 4381251881Speter fb->changed_date, scratch_pool, scratch_pool); 4382251881Speter 4383251881Speter if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED) 4384251881Speter { 4385251881Speter if (eb->notify_func) 4386251881Speter { 4387251881Speter svn_wc_notify_t *notify =svn_wc_create_notify( 4388251881Speter fb->local_abspath, 4389251881Speter svn_wc_notify_update_skip_access_denied, 4390251881Speter scratch_pool); 4391251881Speter 4392251881Speter notify->kind = svn_node_file; 4393251881Speter notify->err = err; 4394251881Speter 4395251881Speter eb->notify_func(eb->notify_baton, notify, scratch_pool); 4396251881Speter } 4397251881Speter svn_error_clear(err); 4398251881Speter 4399251881Speter SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, 4400251881Speter scratch_pool)); 4401251881Speter fb->skip_this = TRUE; 4402251881Speter 4403251881Speter svn_pool_destroy(fb->pool); 4404251881Speter SVN_ERR(maybe_release_dir_info(pdb)); 4405251881Speter return SVN_NO_ERROR; 4406251881Speter } 4407251881Speter else 4408251881Speter SVN_ERR(err); 4409251881Speter 4410251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4411251881Speter scratch_pool); 4412251881Speter } 4413251881Speter else 4414251881Speter { 4415251881Speter install_pristine = FALSE; 4416251881Speter if (fb->new_text_base_sha1_checksum) 4417251881Speter content_state = svn_wc_notify_state_changed; 4418251881Speter else 4419251881Speter content_state = svn_wc_notify_state_unchanged; 4420251881Speter } 4421251881Speter 4422251881Speter if (install_pristine) 4423251881Speter { 4424251881Speter svn_boolean_t record_fileinfo; 4425251881Speter 4426251881Speter /* If we are installing from the pristine contents, then go ahead and 4427251881Speter record the fileinfo. That will be the "proper" values. Installing 4428251881Speter from some random file means the fileinfo does NOT correspond to 4429251881Speter the pristine (in which case, the fileinfo will be cleared for 4430251881Speter safety's sake). */ 4431251881Speter record_fileinfo = (install_from == NULL); 4432251881Speter 4433251881Speter SVN_ERR(svn_wc__wq_build_file_install(&work_item, 4434251881Speter eb->db, 4435251881Speter fb->local_abspath, 4436251881Speter install_from, 4437251881Speter eb->use_commit_times, 4438251881Speter record_fileinfo, 4439251881Speter scratch_pool, scratch_pool)); 4440251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4441251881Speter scratch_pool); 4442251881Speter } 4443251881Speter else if (lock_state == svn_wc_notify_lock_state_unlocked 4444251881Speter && !fb->obstruction_found) 4445251881Speter { 4446251881Speter /* If a lock was removed and we didn't update the text contents, we 4447251881Speter might need to set the file read-only. 4448251881Speter 4449251881Speter Note: this will also update the executable flag, but ... meh. */ 4450251881Speter SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db, 4451251881Speter fb->local_abspath, 4452251881Speter scratch_pool, scratch_pool)); 4453251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4454251881Speter scratch_pool); 4455251881Speter } 4456251881Speter 4457251881Speter if (! install_pristine 4458251881Speter && (content_state == svn_wc_notify_state_unchanged)) 4459251881Speter { 4460251881Speter /* It is safe to keep the current recorded timestamp and size */ 4461251881Speter keep_recorded_info = TRUE; 4462251881Speter } 4463251881Speter 4464251881Speter /* Clean up any temporary files. */ 4465251881Speter 4466251881Speter /* Remove the INSTALL_FROM file, as long as it doesn't refer to the 4467251881Speter working file. */ 4468251881Speter if (install_from != NULL 4469251881Speter && strcmp(install_from, fb->local_abspath) != 0) 4470251881Speter { 4471251881Speter SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db, 4472251881Speter fb->local_abspath, install_from, 4473251881Speter scratch_pool, scratch_pool)); 4474251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4475251881Speter scratch_pool); 4476251881Speter } 4477251881Speter } 4478251881Speter else 4479251881Speter { 4480251881Speter /* Adding or updating a BASE node under a locally added node. */ 4481251881Speter apr_hash_t *fake_actual_props; 4482251881Speter 4483251881Speter if (fb->adding_file) 4484251881Speter fake_actual_props = apr_hash_make(scratch_pool); 4485251881Speter else 4486251881Speter fake_actual_props = current_base_props; 4487251881Speter 4488251881Speter /* Store the incoming props (sent as propchanges) in new_base_props 4489251881Speter and create a set of new actual props to use for notifications */ 4490251881Speter new_base_props = svn_prop__patch(current_base_props, regular_prop_changes, 4491251881Speter scratch_pool); 4492251881Speter SVN_ERR(svn_wc__merge_props(&conflict_skel, 4493251881Speter &prop_state, 4494251881Speter &new_actual_props, 4495251881Speter eb->db, 4496251881Speter fb->local_abspath, 4497251881Speter NULL /* server_baseprops (not merging) */, 4498251881Speter current_base_props /* pristine_props */, 4499251881Speter fake_actual_props /* actual_props */, 4500251881Speter regular_prop_changes, /* propchanges */ 4501251881Speter scratch_pool, 4502251881Speter scratch_pool)); 4503251881Speter 4504251881Speter if (fb->new_text_base_sha1_checksum) 4505251881Speter content_state = svn_wc_notify_state_changed; 4506251881Speter else 4507251881Speter content_state = svn_wc_notify_state_unchanged; 4508251881Speter } 4509251881Speter 4510251881Speter /* Insert/replace the BASE node with all of the new metadata. */ 4511251881Speter 4512251881Speter /* Set the 'checksum' column of the file's BASE_NODE row to 4513251881Speter * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that 4514251881Speter * checksum is already in the pristine store. */ 4515251881Speter new_checksum = fb->new_text_base_sha1_checksum; 4516251881Speter 4517251881Speter /* If we don't have a NEW checksum, then the base must not have changed. 4518251881Speter Just carry over the old checksum. */ 4519251881Speter if (new_checksum == NULL) 4520251881Speter new_checksum = fb->original_checksum; 4521251881Speter 4522251881Speter if (conflict_skel) 4523251881Speter { 4524251881Speter SVN_ERR(complete_conflict(conflict_skel, 4525251881Speter fb->edit_baton, 4526251881Speter fb->local_abspath, 4527251881Speter fb->old_repos_relpath, 4528251881Speter fb->old_revision, 4529289180Speter fb->new_repos_relpath, 4530251881Speter svn_node_file, svn_node_file, 4531289180Speter fb->dir_baton->deletion_conflicts 4532289180Speter ? svn_hash_gets( 4533289180Speter fb->dir_baton->deletion_conflicts, 4534289180Speter fb->name) 4535289180Speter : NULL, 4536251881Speter fb->pool, scratch_pool)); 4537251881Speter 4538251881Speter SVN_ERR(svn_wc__conflict_create_markers(&work_item, 4539251881Speter eb->db, fb->local_abspath, 4540251881Speter conflict_skel, 4541251881Speter scratch_pool, scratch_pool)); 4542251881Speter 4543251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, 4544251881Speter scratch_pool); 4545251881Speter } 4546251881Speter 4547251881Speter /* Any inherited props to be set set for this base node? */ 4548251881Speter if (eb->wcroot_iprops) 4549251881Speter { 4550251881Speter iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath); 4551251881Speter 4552251881Speter /* close_edit may also update iprops for switched nodes, catching 4553251881Speter those for which close_directory is never called (e.g. a switch 4554251881Speter with no changes). So as a minor optimization we remove any 4555251881Speter iprops from the hash so as not to set them again in 4556251881Speter close_edit. */ 4557251881Speter if (iprops) 4558251881Speter svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL); 4559251881Speter } 4560251881Speter 4561251881Speter SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath, 4562251881Speter eb->wcroot_abspath, 4563289180Speter fb->new_repos_relpath, 4564251881Speter eb->repos_root, eb->repos_uuid, 4565251881Speter *eb->target_revision, 4566251881Speter new_base_props, 4567251881Speter fb->changed_rev, 4568251881Speter fb->changed_date, 4569251881Speter fb->changed_author, 4570251881Speter new_checksum, 4571251881Speter (dav_prop_changes->nelts > 0) 4572251881Speter ? svn_prop_array_to_hash( 4573251881Speter dav_prop_changes, 4574251881Speter scratch_pool) 4575251881Speter : NULL, 4576251881Speter (fb->add_existed && fb->adding_file), 4577251881Speter (! fb->shadowed) && new_base_props, 4578251881Speter new_actual_props, 4579251881Speter iprops, 4580251881Speter keep_recorded_info, 4581251881Speter (fb->shadowed && fb->obstruction_found), 4582251881Speter conflict_skel, 4583251881Speter all_work_items, 4584251881Speter scratch_pool)); 4585251881Speter 4586251881Speter if (conflict_skel && eb->conflict_func) 4587251881Speter SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath, 4588289180Speter svn_node_file, 4589251881Speter conflict_skel, 4590251881Speter NULL /* merge_options */, 4591251881Speter eb->conflict_func, 4592251881Speter eb->conflict_baton, 4593251881Speter eb->cancel_func, 4594251881Speter eb->cancel_baton, 4595251881Speter scratch_pool)); 4596251881Speter 4597251881Speter /* Deal with the WORKING tree, based on updates to the BASE tree. */ 4598251881Speter 4599289180Speter svn_hash_sets(fb->dir_baton->not_present_nodes, fb->name, NULL); 4600251881Speter 4601251881Speter /* Send a notification to the callback function. (Skip notifications 4602251881Speter about files which were already notified for another reason.) */ 4603251881Speter if (eb->notify_func && !fb->already_notified 4604251881Speter && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked)) 4605251881Speter { 4606251881Speter svn_wc_notify_t *notify; 4607251881Speter svn_wc_notify_action_t action = svn_wc_notify_update_update; 4608251881Speter 4609251881Speter if (fb->edited) 4610251881Speter { 4611251881Speter if (fb->shadowed || fb->edit_obstructed) 4612251881Speter action = fb->adding_file 4613251881Speter ? svn_wc_notify_update_shadowed_add 4614251881Speter : svn_wc_notify_update_shadowed_update; 4615251881Speter else if (fb->obstruction_found || fb->add_existed) 4616251881Speter { 4617251881Speter if (content_state != svn_wc_notify_state_conflicted) 4618251881Speter action = svn_wc_notify_exists; 4619251881Speter } 4620251881Speter else if (fb->adding_file) 4621251881Speter { 4622251881Speter action = svn_wc_notify_update_add; 4623251881Speter } 4624251881Speter } 4625251881Speter else 4626251881Speter { 4627251881Speter SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked); 4628251881Speter action = svn_wc_notify_update_broken_lock; 4629251881Speter } 4630251881Speter 4631251881Speter /* If the file was moved-away, notify for the moved-away node. 4632251881Speter * The original location only had its BASE info changed and 4633251881Speter * we don't usually notify about such changes. */ 4634251881Speter notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool); 4635251881Speter notify->kind = svn_node_file; 4636251881Speter notify->content_state = content_state; 4637251881Speter notify->prop_state = prop_state; 4638251881Speter notify->lock_state = lock_state; 4639251881Speter notify->revision = *eb->target_revision; 4640251881Speter notify->old_revision = fb->old_revision; 4641251881Speter 4642251881Speter /* Fetch the mimetype from the actual properties */ 4643251881Speter notify->mime_type = svn_prop_get_value(new_actual_props, 4644251881Speter SVN_PROP_MIME_TYPE); 4645251881Speter 4646251881Speter eb->notify_func(eb->notify_baton, notify, scratch_pool); 4647251881Speter } 4648251881Speter 4649251881Speter svn_pool_destroy(fb->pool); /* Destroy scratch_pool */ 4650251881Speter 4651251881Speter /* We have one less referrer to the directory */ 4652251881Speter SVN_ERR(maybe_release_dir_info(pdb)); 4653251881Speter 4654251881Speter return SVN_NO_ERROR; 4655251881Speter} 4656251881Speter 4657251881Speter 4658289180Speter/* Implements svn_wc__proplist_receiver_t. 4659289180Speter * Check for the presence of an svn:keywords property and queues an install_file 4660289180Speter * work queue item if present. Thus, when the work queue is run to complete the 4661289180Speter * switch operation, all files with keywords will go through the translation 4662289180Speter * process so URLs etc are updated. */ 4663289180Speterstatic svn_error_t * 4664289180Speterupdate_keywords_after_switch_cb(void *baton, 4665289180Speter const char *local_abspath, 4666289180Speter apr_hash_t *props, 4667289180Speter apr_pool_t *scratch_pool) 4668289180Speter{ 4669289180Speter struct edit_baton *eb = baton; 4670289180Speter svn_string_t *propval; 4671289180Speter svn_boolean_t modified; 4672289180Speter svn_boolean_t record_fileinfo; 4673289180Speter svn_skel_t *work_items; 4674289180Speter const char *install_from; 4675289180Speter 4676289180Speter propval = svn_hash_gets(props, SVN_PROP_KEYWORDS); 4677289180Speter if (!propval) 4678289180Speter return SVN_NO_ERROR; 4679289180Speter 4680289180Speter SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db, 4681289180Speter local_abspath, FALSE, 4682289180Speter scratch_pool)); 4683289180Speter if (modified) 4684289180Speter { 4685289180Speter const char *temp_dir_abspath; 4686289180Speter svn_stream_t *working_stream; 4687289180Speter svn_stream_t *install_from_stream; 4688289180Speter 4689289180Speter SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, eb->db, 4690289180Speter local_abspath, scratch_pool, 4691289180Speter scratch_pool)); 4692289180Speter SVN_ERR(svn_stream_open_readonly(&working_stream, local_abspath, 4693289180Speter scratch_pool, scratch_pool)); 4694289180Speter SVN_ERR(svn_stream_open_unique(&install_from_stream, &install_from, 4695289180Speter temp_dir_abspath, svn_io_file_del_none, 4696289180Speter scratch_pool, scratch_pool)); 4697289180Speter SVN_ERR(svn_stream_copy3(working_stream, install_from_stream, 4698289180Speter eb->cancel_func, eb->cancel_baton, 4699289180Speter scratch_pool)); 4700289180Speter record_fileinfo = FALSE; 4701289180Speter } 4702289180Speter else 4703289180Speter { 4704289180Speter install_from = NULL; 4705289180Speter record_fileinfo = TRUE; 4706289180Speter } 4707289180Speter 4708289180Speter SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath, 4709289180Speter install_from, 4710289180Speter eb->use_commit_times, 4711289180Speter record_fileinfo, 4712289180Speter scratch_pool, scratch_pool)); 4713289180Speter if (install_from) 4714289180Speter { 4715289180Speter svn_skel_t *work_item; 4716289180Speter 4717289180Speter SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db, 4718289180Speter local_abspath, install_from, 4719289180Speter scratch_pool, scratch_pool)); 4720289180Speter work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 4721289180Speter } 4722289180Speter 4723289180Speter SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items, 4724289180Speter scratch_pool)); 4725289180Speter 4726289180Speter return SVN_NO_ERROR; 4727289180Speter} 4728289180Speter 4729289180Speter 4730251881Speter/* An svn_delta_editor_t function. */ 4731251881Speterstatic svn_error_t * 4732251881Speterclose_edit(void *edit_baton, 4733251881Speter apr_pool_t *pool) 4734251881Speter{ 4735251881Speter struct edit_baton *eb = edit_baton; 4736251881Speter apr_pool_t *scratch_pool = eb->pool; 4737251881Speter 4738251881Speter /* The editor didn't even open the root; we have to take care of 4739251881Speter some cleanup stuffs. */ 4740251881Speter if (! eb->root_opened 4741251881Speter && *eb->target_basename == '\0') 4742251881Speter { 4743251881Speter /* We need to "un-incomplete" the root directory. */ 4744251881Speter SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db, 4745251881Speter eb->anchor_abspath, 4746251881Speter scratch_pool)); 4747251881Speter } 4748251881Speter 4749251881Speter /* By definition, anybody "driving" this editor for update or switch 4750251881Speter purposes at a *minimum* must have called set_target_revision() at 4751251881Speter the outset, and close_edit() at the end -- even if it turned out 4752251881Speter that no changes ever had to be made, and open_root() was never 4753251881Speter called. That's fine. But regardless, when the edit is over, 4754251881Speter this editor needs to make sure that *all* paths have had their 4755251881Speter revisions bumped to the new target revision. */ 4756251881Speter 4757251881Speter /* Make sure our update target now has the new working revision. 4758251881Speter Also, if this was an 'svn switch', then rewrite the target's 4759251881Speter url. All of this tweaking might happen recursively! Note 4760251881Speter that if eb->target is NULL, that's okay (albeit "sneaky", 4761251881Speter some might say). */ 4762251881Speter 4763251881Speter /* Extra check: if the update did nothing but make its target 4764251881Speter 'deleted', then do *not* run cleanup on the target, as it 4765251881Speter will only remove the deleted entry! */ 4766251881Speter if (! eb->target_deleted) 4767251881Speter { 4768251881Speter SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db, 4769251881Speter eb->target_abspath, 4770251881Speter eb->requested_depth, 4771289180Speter eb->switch_repos_relpath, 4772251881Speter eb->repos_root, 4773251881Speter eb->repos_uuid, 4774251881Speter *(eb->target_revision), 4775251881Speter eb->skipped_trees, 4776251881Speter eb->wcroot_iprops, 4777289180Speter ! eb->edited, 4778251881Speter eb->notify_func, 4779251881Speter eb->notify_baton, 4780251881Speter eb->pool)); 4781251881Speter 4782251881Speter if (*eb->target_basename != '\0') 4783251881Speter { 4784251881Speter svn_wc__db_status_t status; 4785251881Speter svn_error_t *err; 4786251881Speter 4787251881Speter /* Note: we are fetching information about the *target*, not anchor. 4788251881Speter There is no guarantee that the target has a BASE node. 4789251881Speter For example: 4790251881Speter 4791251881Speter The node was not present in BASE, but locally-added, and the 4792251881Speter update did not create a new BASE node "under" the local-add. 4793251881Speter 4794251881Speter If there is no BASE node for the target, then we certainly don't 4795251881Speter have to worry about removing it. */ 4796251881Speter err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL, 4797251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 4798251881Speter NULL, NULL, NULL, NULL, 4799251881Speter eb->db, eb->target_abspath, 4800251881Speter scratch_pool, scratch_pool); 4801251881Speter if (err) 4802251881Speter { 4803251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 4804251881Speter return svn_error_trace(err); 4805251881Speter 4806251881Speter svn_error_clear(err); 4807251881Speter } 4808251881Speter else if (status == svn_wc__db_status_excluded) 4809251881Speter { 4810251881Speter /* There is a small chance that the explicit target of an update/ 4811251881Speter switch is gone in the repository, in that specific case the 4812251881Speter node hasn't been re-added to the BASE tree by this update. 4813251881Speter 4814251881Speter If so, we should get rid of this excluded node now. */ 4815251881Speter 4816251881Speter SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath, 4817289180Speter TRUE, FALSE, FALSE, 4818251881Speter SVN_INVALID_REVNUM, 4819251881Speter NULL, NULL, scratch_pool)); 4820251881Speter } 4821251881Speter } 4822251881Speter } 4823251881Speter 4824289180Speter /* Update keywords in switched files. 4825289180Speter GOTO #1975 (the year of the Altair 8800). */ 4826289180Speter if (eb->switch_repos_relpath) 4827289180Speter { 4828289180Speter svn_depth_t depth; 4829289180Speter 4830289180Speter if (eb->requested_depth > svn_depth_empty) 4831289180Speter depth = eb->requested_depth; 4832289180Speter else 4833289180Speter depth = svn_depth_infinity; 4834289180Speter 4835289180Speter SVN_ERR(svn_wc__db_read_props_streamily(eb->db, 4836289180Speter eb->target_abspath, 4837289180Speter depth, 4838289180Speter FALSE, /* pristine */ 4839289180Speter NULL, /* changelists */ 4840289180Speter update_keywords_after_switch_cb, 4841289180Speter eb, 4842289180Speter eb->cancel_func, 4843289180Speter eb->cancel_baton, 4844289180Speter scratch_pool)); 4845289180Speter } 4846289180Speter 4847251881Speter /* The edit is over: run the wq with proper cancel support, 4848251881Speter but first kill the handler that would run it on the pool 4849251881Speter cleanup at the end of this function. */ 4850251881Speter apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton); 4851251881Speter 4852251881Speter SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath, 4853251881Speter eb->cancel_func, eb->cancel_baton, 4854251881Speter eb->pool)); 4855251881Speter 4856251881Speter /* The edit is over, free its pool. 4857251881Speter ### No, this is wrong. Who says this editor/baton won't be used 4858251881Speter again? But the change is not merely to remove this call. We 4859251881Speter should also make eb->pool not be a subpool (see make_editor), 4860251881Speter and change callers of svn_client_{checkout,update,switch} to do 4861251881Speter better pool management. ### */ 4862251881Speter 4863251881Speter svn_pool_destroy(eb->pool); 4864251881Speter 4865251881Speter return SVN_NO_ERROR; 4866251881Speter} 4867251881Speter 4868251881Speter 4869251881Speter/*** Returning editors. ***/ 4870251881Speter 4871251881Speter/* Helper for the three public editor-supplying functions. */ 4872251881Speterstatic svn_error_t * 4873251881Spetermake_editor(svn_revnum_t *target_revision, 4874251881Speter svn_wc__db_t *db, 4875251881Speter const char *anchor_abspath, 4876251881Speter const char *target_basename, 4877251881Speter apr_hash_t *wcroot_iprops, 4878251881Speter svn_boolean_t use_commit_times, 4879251881Speter const char *switch_url, 4880251881Speter svn_depth_t depth, 4881251881Speter svn_boolean_t depth_is_sticky, 4882251881Speter svn_boolean_t allow_unver_obstructions, 4883251881Speter svn_boolean_t adds_as_modification, 4884251881Speter svn_boolean_t server_performs_filtering, 4885251881Speter svn_boolean_t clean_checkout, 4886251881Speter svn_wc_notify_func2_t notify_func, 4887251881Speter void *notify_baton, 4888251881Speter svn_cancel_func_t cancel_func, 4889251881Speter void *cancel_baton, 4890251881Speter svn_wc_dirents_func_t fetch_dirents_func, 4891251881Speter void *fetch_dirents_baton, 4892251881Speter svn_wc_conflict_resolver_func2_t conflict_func, 4893251881Speter void *conflict_baton, 4894251881Speter svn_wc_external_update_t external_func, 4895251881Speter void *external_baton, 4896251881Speter const char *diff3_cmd, 4897251881Speter const apr_array_header_t *preserved_exts, 4898251881Speter const svn_delta_editor_t **editor, 4899251881Speter void **edit_baton, 4900251881Speter apr_pool_t *result_pool, 4901251881Speter apr_pool_t *scratch_pool) 4902251881Speter{ 4903251881Speter struct edit_baton *eb; 4904251881Speter void *inner_baton; 4905251881Speter apr_pool_t *edit_pool = svn_pool_create(result_pool); 4906251881Speter svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool); 4907251881Speter const svn_delta_editor_t *inner_editor; 4908251881Speter const char *repos_root, *repos_uuid; 4909251881Speter struct svn_wc__shim_fetch_baton_t *sfb; 4910251881Speter svn_delta_shim_callbacks_t *shim_callbacks = 4911251881Speter svn_delta_shim_callbacks_default(edit_pool); 4912251881Speter 4913251881Speter /* An unknown depth can't be sticky. */ 4914251881Speter if (depth == svn_depth_unknown) 4915251881Speter depth_is_sticky = FALSE; 4916251881Speter 4917251881Speter /* Get the anchor's repository root and uuid. The anchor must already exist 4918251881Speter in BASE. */ 4919289180Speter SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, &repos_root, 4920289180Speter &repos_uuid, NULL, NULL, NULL, NULL, 4921289180Speter NULL, NULL, NULL, NULL, NULL, NULL, 4922289180Speter db, anchor_abspath, 4923289180Speter result_pool, scratch_pool)); 4924251881Speter 4925251881Speter /* With WC-NG we need a valid repository root */ 4926251881Speter SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL); 4927251881Speter 4928251881Speter /* Disallow a switch operation to change the repository root of the target, 4929251881Speter if that is known. */ 4930251881Speter if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url)) 4931251881Speter return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL, 4932251881Speter _("'%s'\nis not the same repository as\n'%s'"), 4933251881Speter switch_url, repos_root); 4934251881Speter 4935251881Speter /* Construct an edit baton. */ 4936251881Speter eb = apr_pcalloc(edit_pool, sizeof(*eb)); 4937251881Speter eb->pool = edit_pool; 4938251881Speter eb->use_commit_times = use_commit_times; 4939251881Speter eb->target_revision = target_revision; 4940251881Speter eb->repos_root = repos_root; 4941251881Speter eb->repos_uuid = repos_uuid; 4942251881Speter eb->db = db; 4943251881Speter eb->target_basename = target_basename; 4944251881Speter eb->anchor_abspath = anchor_abspath; 4945251881Speter eb->wcroot_iprops = wcroot_iprops; 4946251881Speter 4947251881Speter SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath, 4948251881Speter edit_pool, scratch_pool)); 4949251881Speter 4950251881Speter if (switch_url) 4951289180Speter eb->switch_repos_relpath = 4952251881Speter svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool); 4953251881Speter else 4954289180Speter eb->switch_repos_relpath = NULL; 4955251881Speter 4956251881Speter if (svn_path_is_empty(target_basename)) 4957251881Speter eb->target_abspath = eb->anchor_abspath; 4958251881Speter else 4959251881Speter eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename, 4960251881Speter edit_pool); 4961251881Speter 4962251881Speter eb->requested_depth = depth; 4963251881Speter eb->depth_is_sticky = depth_is_sticky; 4964251881Speter eb->notify_func = notify_func; 4965251881Speter eb->notify_baton = notify_baton; 4966251881Speter eb->external_func = external_func; 4967251881Speter eb->external_baton = external_baton; 4968251881Speter eb->diff3_cmd = diff3_cmd; 4969251881Speter eb->cancel_func = cancel_func; 4970251881Speter eb->cancel_baton = cancel_baton; 4971251881Speter eb->conflict_func = conflict_func; 4972251881Speter eb->conflict_baton = conflict_baton; 4973251881Speter eb->allow_unver_obstructions = allow_unver_obstructions; 4974251881Speter eb->adds_as_modification = adds_as_modification; 4975251881Speter eb->clean_checkout = clean_checkout; 4976251881Speter eb->skipped_trees = apr_hash_make(edit_pool); 4977251881Speter eb->dir_dirents = apr_hash_make(edit_pool); 4978251881Speter eb->ext_patterns = preserved_exts; 4979251881Speter 4980251881Speter apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton, 4981251881Speter apr_pool_cleanup_null); 4982251881Speter 4983251881Speter /* Construct an editor. */ 4984251881Speter tree_editor->set_target_revision = set_target_revision; 4985251881Speter tree_editor->open_root = open_root; 4986251881Speter tree_editor->delete_entry = delete_entry; 4987251881Speter tree_editor->add_directory = add_directory; 4988251881Speter tree_editor->open_directory = open_directory; 4989251881Speter tree_editor->change_dir_prop = change_dir_prop; 4990251881Speter tree_editor->close_directory = close_directory; 4991251881Speter tree_editor->absent_directory = absent_directory; 4992251881Speter tree_editor->add_file = add_file; 4993251881Speter tree_editor->open_file = open_file; 4994251881Speter tree_editor->apply_textdelta = apply_textdelta; 4995251881Speter tree_editor->change_file_prop = change_file_prop; 4996251881Speter tree_editor->close_file = close_file; 4997251881Speter tree_editor->absent_file = absent_file; 4998251881Speter tree_editor->close_edit = close_edit; 4999251881Speter 5000251881Speter /* Fiddle with the type system. */ 5001251881Speter inner_editor = tree_editor; 5002251881Speter inner_baton = eb; 5003251881Speter 5004251881Speter if (!depth_is_sticky 5005251881Speter && depth != svn_depth_unknown 5006251881Speter && svn_depth_empty <= depth && depth < svn_depth_infinity 5007251881Speter && fetch_dirents_func) 5008251881Speter { 5009251881Speter /* We are asked to perform an update at a depth less than the ambient 5010251881Speter depth. In this case the update won't describe additions that would 5011251881Speter have been reported if we updated at the ambient depth. */ 5012251881Speter svn_error_t *err; 5013251881Speter svn_node_kind_t dir_kind; 5014251881Speter svn_wc__db_status_t dir_status; 5015251881Speter const char *dir_repos_relpath; 5016251881Speter svn_depth_t dir_depth; 5017251881Speter 5018251881Speter /* we have to do this on the target of the update, not the anchor */ 5019251881Speter err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL, 5020251881Speter &dir_repos_relpath, NULL, NULL, NULL, 5021251881Speter NULL, NULL, &dir_depth, NULL, NULL, NULL, 5022251881Speter NULL, NULL, NULL, 5023251881Speter db, eb->target_abspath, 5024251881Speter scratch_pool, scratch_pool); 5025251881Speter 5026251881Speter if (!err 5027251881Speter && dir_kind == svn_node_dir 5028251881Speter && dir_status == svn_wc__db_status_normal) 5029251881Speter { 5030251881Speter if (dir_depth > depth) 5031251881Speter { 5032251881Speter apr_hash_t *dirents; 5033251881Speter 5034251881Speter /* If we switch, we should look at the new relpath */ 5035289180Speter if (eb->switch_repos_relpath) 5036289180Speter dir_repos_relpath = eb->switch_repos_relpath; 5037251881Speter 5038251881Speter SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, 5039251881Speter repos_root, dir_repos_relpath, 5040251881Speter edit_pool, scratch_pool)); 5041251881Speter 5042251881Speter if (dirents != NULL && apr_hash_count(dirents)) 5043251881Speter svn_hash_sets(eb->dir_dirents, 5044251881Speter apr_pstrdup(edit_pool, dir_repos_relpath), 5045251881Speter dirents); 5046251881Speter } 5047251881Speter 5048251881Speter if (depth == svn_depth_immediates) 5049251881Speter { 5050251881Speter /* Worst case scenario of issue #3569 fix: We have to do the 5051251881Speter same for all existing subdirs, but then we check for 5052251881Speter svn_depth_empty. */ 5053251881Speter const apr_array_header_t *children; 5054251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 5055251881Speter int i; 5056251881Speter SVN_ERR(svn_wc__db_base_get_children(&children, db, 5057251881Speter eb->target_abspath, 5058251881Speter scratch_pool, 5059251881Speter iterpool)); 5060251881Speter 5061251881Speter for (i = 0; i < children->nelts; i++) 5062251881Speter { 5063251881Speter const char *child_abspath; 5064251881Speter const char *child_name; 5065251881Speter 5066251881Speter svn_pool_clear(iterpool); 5067251881Speter 5068251881Speter child_name = APR_ARRAY_IDX(children, i, const char *); 5069251881Speter 5070251881Speter child_abspath = svn_dirent_join(eb->target_abspath, 5071251881Speter child_name, iterpool); 5072251881Speter 5073251881Speter SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind, 5074251881Speter NULL, &dir_repos_relpath, 5075251881Speter NULL, NULL, NULL, NULL, 5076251881Speter NULL, &dir_depth, NULL, 5077251881Speter NULL, NULL, NULL, NULL, 5078251881Speter NULL, 5079251881Speter db, child_abspath, 5080251881Speter iterpool, iterpool)); 5081251881Speter 5082251881Speter if (dir_kind == svn_node_dir 5083251881Speter && dir_status == svn_wc__db_status_normal 5084251881Speter && dir_depth > svn_depth_empty) 5085251881Speter { 5086251881Speter apr_hash_t *dirents; 5087251881Speter 5088251881Speter /* If we switch, we should look at the new relpath */ 5089289180Speter if (eb->switch_repos_relpath) 5090251881Speter dir_repos_relpath = svn_relpath_join( 5091289180Speter eb->switch_repos_relpath, 5092251881Speter child_name, iterpool); 5093251881Speter 5094251881Speter SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents, 5095251881Speter repos_root, dir_repos_relpath, 5096251881Speter edit_pool, iterpool)); 5097251881Speter 5098251881Speter if (dirents != NULL && apr_hash_count(dirents)) 5099251881Speter svn_hash_sets(eb->dir_dirents, 5100251881Speter apr_pstrdup(edit_pool, 5101251881Speter dir_repos_relpath), 5102251881Speter dirents); 5103251881Speter } 5104251881Speter } 5105251881Speter } 5106251881Speter } 5107251881Speter else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 5108251881Speter svn_error_clear(err); 5109251881Speter else 5110251881Speter SVN_ERR(err); 5111251881Speter } 5112251881Speter 5113251881Speter /* We need to limit the scope of our operation to the ambient depths 5114251881Speter present in the working copy already, but only if the requested 5115251881Speter depth is not sticky. If a depth was explicitly requested, 5116251881Speter libsvn_delta/depth_filter_editor.c will ensure that we never see 5117251881Speter editor calls that extend beyond the scope of the requested depth. 5118251881Speter But even what we do so might extend beyond the scope of our 5119251881Speter ambient depth. So we use another filtering editor to avoid 5120251881Speter modifying the ambient working copy depth when not asked to do so. 5121251881Speter (This can also be skipped if the server understands depth.) */ 5122251881Speter if (!server_performs_filtering 5123251881Speter && !depth_is_sticky) 5124251881Speter SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor, 5125251881Speter &inner_baton, 5126251881Speter db, 5127251881Speter anchor_abspath, 5128251881Speter target_basename, 5129251881Speter inner_editor, 5130251881Speter inner_baton, 5131251881Speter result_pool)); 5132251881Speter 5133251881Speter SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, 5134251881Speter cancel_baton, 5135251881Speter inner_editor, 5136251881Speter inner_baton, 5137251881Speter editor, 5138251881Speter edit_baton, 5139251881Speter result_pool)); 5140251881Speter 5141251881Speter sfb = apr_palloc(result_pool, sizeof(*sfb)); 5142251881Speter sfb->db = db; 5143251881Speter sfb->base_abspath = eb->anchor_abspath; 5144251881Speter sfb->fetch_base = TRUE; 5145251881Speter 5146251881Speter shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func; 5147251881Speter shim_callbacks->fetch_props_func = svn_wc__fetch_props_func; 5148251881Speter shim_callbacks->fetch_base_func = svn_wc__fetch_base_func; 5149251881Speter shim_callbacks->fetch_baton = sfb; 5150251881Speter 5151251881Speter SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, 5152251881Speter NULL, NULL, shim_callbacks, 5153251881Speter result_pool, scratch_pool)); 5154251881Speter 5155251881Speter return SVN_NO_ERROR; 5156251881Speter} 5157251881Speter 5158251881Speter 5159251881Spetersvn_error_t * 5160251881Spetersvn_wc__get_update_editor(const svn_delta_editor_t **editor, 5161251881Speter void **edit_baton, 5162251881Speter svn_revnum_t *target_revision, 5163251881Speter svn_wc_context_t *wc_ctx, 5164251881Speter const char *anchor_abspath, 5165251881Speter const char *target_basename, 5166251881Speter apr_hash_t *wcroot_iprops, 5167251881Speter svn_boolean_t use_commit_times, 5168251881Speter svn_depth_t depth, 5169251881Speter svn_boolean_t depth_is_sticky, 5170251881Speter svn_boolean_t allow_unver_obstructions, 5171251881Speter svn_boolean_t adds_as_modification, 5172251881Speter svn_boolean_t server_performs_filtering, 5173251881Speter svn_boolean_t clean_checkout, 5174251881Speter const char *diff3_cmd, 5175251881Speter const apr_array_header_t *preserved_exts, 5176251881Speter svn_wc_dirents_func_t fetch_dirents_func, 5177251881Speter void *fetch_dirents_baton, 5178251881Speter svn_wc_conflict_resolver_func2_t conflict_func, 5179251881Speter void *conflict_baton, 5180251881Speter svn_wc_external_update_t external_func, 5181251881Speter void *external_baton, 5182251881Speter svn_cancel_func_t cancel_func, 5183251881Speter void *cancel_baton, 5184251881Speter svn_wc_notify_func2_t notify_func, 5185251881Speter void *notify_baton, 5186251881Speter apr_pool_t *result_pool, 5187251881Speter apr_pool_t *scratch_pool) 5188251881Speter{ 5189251881Speter return make_editor(target_revision, wc_ctx->db, anchor_abspath, 5190251881Speter target_basename, wcroot_iprops, use_commit_times, 5191251881Speter NULL, depth, depth_is_sticky, allow_unver_obstructions, 5192251881Speter adds_as_modification, server_performs_filtering, 5193251881Speter clean_checkout, 5194251881Speter notify_func, notify_baton, 5195251881Speter cancel_func, cancel_baton, 5196251881Speter fetch_dirents_func, fetch_dirents_baton, 5197251881Speter conflict_func, conflict_baton, 5198251881Speter external_func, external_baton, 5199251881Speter diff3_cmd, preserved_exts, editor, edit_baton, 5200251881Speter result_pool, scratch_pool); 5201251881Speter} 5202251881Speter 5203251881Spetersvn_error_t * 5204251881Spetersvn_wc__get_switch_editor(const svn_delta_editor_t **editor, 5205251881Speter void **edit_baton, 5206251881Speter svn_revnum_t *target_revision, 5207251881Speter svn_wc_context_t *wc_ctx, 5208251881Speter const char *anchor_abspath, 5209251881Speter const char *target_basename, 5210251881Speter const char *switch_url, 5211251881Speter apr_hash_t *wcroot_iprops, 5212251881Speter svn_boolean_t use_commit_times, 5213251881Speter svn_depth_t depth, 5214251881Speter svn_boolean_t depth_is_sticky, 5215251881Speter svn_boolean_t allow_unver_obstructions, 5216251881Speter svn_boolean_t server_performs_filtering, 5217251881Speter const char *diff3_cmd, 5218251881Speter const apr_array_header_t *preserved_exts, 5219251881Speter svn_wc_dirents_func_t fetch_dirents_func, 5220251881Speter void *fetch_dirents_baton, 5221251881Speter svn_wc_conflict_resolver_func2_t conflict_func, 5222251881Speter void *conflict_baton, 5223251881Speter svn_wc_external_update_t external_func, 5224251881Speter void *external_baton, 5225251881Speter svn_cancel_func_t cancel_func, 5226251881Speter void *cancel_baton, 5227251881Speter svn_wc_notify_func2_t notify_func, 5228251881Speter void *notify_baton, 5229251881Speter apr_pool_t *result_pool, 5230251881Speter apr_pool_t *scratch_pool) 5231251881Speter{ 5232251881Speter SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool)); 5233251881Speter 5234251881Speter return make_editor(target_revision, wc_ctx->db, anchor_abspath, 5235251881Speter target_basename, wcroot_iprops, use_commit_times, 5236251881Speter switch_url, 5237251881Speter depth, depth_is_sticky, allow_unver_obstructions, 5238251881Speter FALSE /* adds_as_modification */, 5239251881Speter server_performs_filtering, 5240251881Speter FALSE /* clean_checkout */, 5241251881Speter notify_func, notify_baton, 5242251881Speter cancel_func, cancel_baton, 5243251881Speter fetch_dirents_func, fetch_dirents_baton, 5244251881Speter conflict_func, conflict_baton, 5245251881Speter external_func, external_baton, 5246251881Speter diff3_cmd, preserved_exts, 5247251881Speter editor, edit_baton, 5248251881Speter result_pool, scratch_pool); 5249251881Speter} 5250251881Speter 5251251881Speter 5252251881Speter 5253251881Speter/* ### Note that this function is completely different from the rest of the 5254251881Speter update editor in what it updates. The update editor changes only BASE 5255251881Speter and ACTUAL and this function just changes WORKING and ACTUAL. 5256251881Speter 5257251881Speter In the entries world this function shared a lot of code with the 5258251881Speter update editor but in the wonderful new WC-NG world it will probably 5259251881Speter do more and more by itself and would be more logically grouped with 5260251881Speter the add/copy functionality in adm_ops.c and copy.c. */ 5261251881Spetersvn_error_t * 5262251881Spetersvn_wc_add_repos_file4(svn_wc_context_t *wc_ctx, 5263251881Speter const char *local_abspath, 5264251881Speter svn_stream_t *new_base_contents, 5265251881Speter svn_stream_t *new_contents, 5266251881Speter apr_hash_t *new_base_props, 5267251881Speter apr_hash_t *new_props, 5268251881Speter const char *copyfrom_url, 5269251881Speter svn_revnum_t copyfrom_rev, 5270251881Speter svn_cancel_func_t cancel_func, 5271251881Speter void *cancel_baton, 5272251881Speter apr_pool_t *scratch_pool) 5273251881Speter{ 5274251881Speter svn_wc__db_t *db = wc_ctx->db; 5275251881Speter const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 5276251881Speter svn_wc__db_status_t status; 5277251881Speter svn_node_kind_t kind; 5278251881Speter const char *tmp_text_base_abspath; 5279251881Speter svn_checksum_t *new_text_base_md5_checksum; 5280251881Speter svn_checksum_t *new_text_base_sha1_checksum; 5281251881Speter const char *source_abspath = NULL; 5282251881Speter svn_skel_t *all_work_items = NULL; 5283251881Speter svn_skel_t *work_item; 5284251881Speter const char *repos_root_url; 5285251881Speter const char *repos_uuid; 5286251881Speter const char *original_repos_relpath; 5287251881Speter svn_revnum_t changed_rev; 5288251881Speter apr_time_t changed_date; 5289251881Speter const char *changed_author; 5290289180Speter svn_stream_t *tmp_base_contents; 5291289180Speter svn_wc__db_install_data_t *install_data; 5292251881Speter svn_error_t *err; 5293251881Speter apr_pool_t *pool = scratch_pool; 5294251881Speter 5295251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 5296251881Speter SVN_ERR_ASSERT(new_base_contents != NULL); 5297251881Speter SVN_ERR_ASSERT(new_base_props != NULL); 5298251881Speter 5299251881Speter /* We should have a write lock on this file's parent directory. */ 5300251881Speter SVN_ERR(svn_wc__write_check(db, dir_abspath, pool)); 5301251881Speter 5302251881Speter err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5303251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5304251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5305251881Speter NULL, NULL, NULL, 5306251881Speter db, local_abspath, scratch_pool, scratch_pool); 5307251881Speter 5308251881Speter if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 5309251881Speter return svn_error_trace(err); 5310251881Speter else if(err) 5311251881Speter svn_error_clear(err); 5312251881Speter else 5313251881Speter switch (status) 5314251881Speter { 5315251881Speter case svn_wc__db_status_not_present: 5316251881Speter case svn_wc__db_status_deleted: 5317251881Speter break; 5318251881Speter default: 5319251881Speter return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, 5320251881Speter _("Node '%s' exists."), 5321251881Speter svn_dirent_local_style(local_abspath, 5322251881Speter scratch_pool)); 5323251881Speter } 5324251881Speter 5325251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url, 5326251881Speter &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, 5327251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5328251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 5329251881Speter db, dir_abspath, scratch_pool, scratch_pool)); 5330251881Speter 5331251881Speter switch (status) 5332251881Speter { 5333251881Speter case svn_wc__db_status_normal: 5334251881Speter case svn_wc__db_status_added: 5335251881Speter break; 5336251881Speter case svn_wc__db_status_deleted: 5337251881Speter return 5338251881Speter svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL, 5339251881Speter _("Can't add '%s' to a parent directory" 5340251881Speter " scheduled for deletion"), 5341251881Speter svn_dirent_local_style(local_abspath, 5342251881Speter scratch_pool)); 5343251881Speter default: 5344251881Speter return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err, 5345251881Speter _("Can't find parent directory's node while" 5346251881Speter " trying to add '%s'"), 5347251881Speter svn_dirent_local_style(local_abspath, 5348251881Speter scratch_pool)); 5349251881Speter } 5350251881Speter if (kind != svn_node_dir) 5351251881Speter return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 5352251881Speter _("Can't schedule an addition of '%s'" 5353251881Speter " below a not-directory node"), 5354251881Speter svn_dirent_local_style(local_abspath, 5355251881Speter scratch_pool)); 5356251881Speter 5357251881Speter /* Fabricate the anticipated new URL of the target and check the 5358251881Speter copyfrom URL to be in the same repository. */ 5359251881Speter if (copyfrom_url != NULL) 5360251881Speter { 5361251881Speter /* Find the repository_root via the parent directory, which 5362251881Speter is always versioned before this function is called */ 5363251881Speter 5364251881Speter if (!repos_root_url) 5365251881Speter { 5366251881Speter /* The parent is an addition, scan upwards to find the right info */ 5367251881Speter SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, 5368251881Speter &repos_root_url, &repos_uuid, 5369251881Speter NULL, NULL, NULL, NULL, 5370251881Speter wc_ctx->db, dir_abspath, 5371251881Speter scratch_pool, scratch_pool)); 5372251881Speter } 5373251881Speter SVN_ERR_ASSERT(repos_root_url); 5374251881Speter 5375251881Speter original_repos_relpath = 5376251881Speter svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool); 5377251881Speter 5378251881Speter if (!original_repos_relpath) 5379251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 5380251881Speter _("Copyfrom-url '%s' has different repository" 5381251881Speter " root than '%s'"), 5382251881Speter copyfrom_url, repos_root_url); 5383251881Speter } 5384251881Speter else 5385251881Speter { 5386251881Speter original_repos_relpath = NULL; 5387251881Speter copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */ 5388251881Speter } 5389251881Speter 5390251881Speter /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and 5391251881Speter filter NEW_BASE_PROPS so it contains only regular props. */ 5392251881Speter { 5393251881Speter apr_array_header_t *regular_props; 5394251881Speter apr_array_header_t *entry_props; 5395251881Speter 5396251881Speter SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool), 5397251881Speter &entry_props, NULL, ®ular_props, 5398251881Speter pool)); 5399251881Speter 5400251881Speter /* Put regular props back into a hash table. */ 5401251881Speter new_base_props = svn_prop_array_to_hash(regular_props, pool); 5402251881Speter 5403251881Speter /* Get the change_* info from the entry props. */ 5404251881Speter SVN_ERR(accumulate_last_change(&changed_rev, 5405251881Speter &changed_date, 5406251881Speter &changed_author, 5407251881Speter entry_props, pool, pool)); 5408251881Speter } 5409251881Speter 5410251881Speter /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to 5411251881Speter it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its 5412251881Speter NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */ 5413289180Speter if (copyfrom_url) 5414289180Speter { 5415289180Speter SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents, 5416289180Speter &install_data, 5417289180Speter &new_text_base_sha1_checksum, 5418289180Speter &new_text_base_md5_checksum, 5419289180Speter wc_ctx->db, local_abspath, 5420289180Speter scratch_pool, scratch_pool)); 5421289180Speter } 5422289180Speter else 5423289180Speter { 5424289180Speter const char *tmp_dir_abspath; 5425251881Speter 5426289180Speter /* We are not installing a PRISTINE file, but we use the same code to 5427289180Speter create whatever we want to install */ 5428251881Speter 5429289180Speter SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath, 5430289180Speter db, dir_abspath, 5431289180Speter scratch_pool, scratch_pool)); 5432289180Speter 5433289180Speter SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath, 5434289180Speter tmp_dir_abspath, svn_io_file_del_none, 5435289180Speter scratch_pool, scratch_pool)); 5436289180Speter 5437289180Speter new_text_base_sha1_checksum = NULL; 5438289180Speter new_text_base_md5_checksum = NULL; 5439289180Speter } 5440289180Speter SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents, 5441289180Speter cancel_func, cancel_baton, pool)); 5442289180Speter 5443251881Speter /* If the caller gave us a new working file, copy it to a safe (temporary) 5444251881Speter location and set SOURCE_ABSPATH to that path. We'll then translate/copy 5445251881Speter that into place after the node's state has been created. */ 5446251881Speter if (new_contents) 5447251881Speter { 5448251881Speter const char *temp_dir_abspath; 5449251881Speter svn_stream_t *tmp_contents; 5450251881Speter 5451251881Speter SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, 5452251881Speter local_abspath, pool, pool)); 5453251881Speter SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath, 5454251881Speter temp_dir_abspath, svn_io_file_del_none, 5455251881Speter pool, pool)); 5456251881Speter SVN_ERR(svn_stream_copy3(new_contents, tmp_contents, 5457251881Speter cancel_func, cancel_baton, pool)); 5458251881Speter } 5459251881Speter 5460251881Speter /* Install new text base for copied files. Added files do NOT have a 5461251881Speter text base. */ 5462251881Speter if (copyfrom_url != NULL) 5463251881Speter { 5464289180Speter SVN_ERR(svn_wc__db_pristine_install(install_data, 5465251881Speter new_text_base_sha1_checksum, 5466251881Speter new_text_base_md5_checksum, pool)); 5467251881Speter } 5468251881Speter else 5469251881Speter { 5470251881Speter /* ### There's something wrong around here. Sometimes (merge from a 5471251881Speter foreign repository, at least) we are called with copyfrom_url = 5472251881Speter NULL and an empty new_base_contents (and an empty set of 5473251881Speter new_base_props). Why an empty "new base"? 5474251881Speter 5475251881Speter That happens in merge_tests.py 54,87,88,89,143. 5476251881Speter 5477251881Speter In that case, having been given this supposed "new base" file, we 5478251881Speter copy it and calculate its checksum but do not install it. Why? 5479251881Speter That must be wrong. 5480251881Speter 5481251881Speter To crudely work around one issue with this, that we shouldn't 5482251881Speter record a checksum in the database if we haven't installed the 5483251881Speter corresponding pristine text, for now we'll just set the checksum 5484251881Speter to NULL. 5485251881Speter 5486251881Speter The proper solution is probably more like: the caller should pass 5487251881Speter NULL for the missing information, and this function should learn to 5488251881Speter handle that. */ 5489251881Speter 5490251881Speter new_text_base_sha1_checksum = NULL; 5491251881Speter new_text_base_md5_checksum = NULL; 5492251881Speter } 5493251881Speter 5494251881Speter /* For added files without NEW_CONTENTS, then generate the working file 5495251881Speter from the provided "pristine" contents. */ 5496251881Speter if (new_contents == NULL && copyfrom_url == NULL) 5497251881Speter source_abspath = tmp_text_base_abspath; 5498251881Speter 5499251881Speter { 5500251881Speter svn_boolean_t record_fileinfo; 5501251881Speter 5502251881Speter /* If new contents were provided, then we do NOT want to record the 5503251881Speter file information. We assume the new contents do not match the 5504251881Speter "proper" values for RECORDED_SIZE and RECORDED_TIME. */ 5505251881Speter record_fileinfo = (new_contents == NULL); 5506251881Speter 5507251881Speter /* Install the working copy file (with appropriate translation) from 5508251881Speter the appropriate source. SOURCE_ABSPATH will be NULL, indicating an 5509251881Speter installation from the pristine (available for copied/moved files), 5510251881Speter or it will specify a temporary file where we placed a "pristine" 5511251881Speter (for an added file) or a detranslated local-mods file. */ 5512251881Speter SVN_ERR(svn_wc__wq_build_file_install(&work_item, 5513251881Speter db, local_abspath, 5514251881Speter source_abspath, 5515251881Speter FALSE /* use_commit_times */, 5516251881Speter record_fileinfo, 5517251881Speter pool, pool)); 5518251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); 5519251881Speter 5520251881Speter /* If we installed from somewhere besides the official pristine, then 5521251881Speter it is a temporary file, which needs to be removed. */ 5522251881Speter if (source_abspath != NULL) 5523251881Speter { 5524251881Speter SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath, 5525251881Speter source_abspath, 5526251881Speter pool, pool)); 5527251881Speter all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool); 5528251881Speter } 5529251881Speter } 5530251881Speter 5531251881Speter SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath, 5532251881Speter new_base_props, 5533251881Speter changed_rev, 5534251881Speter changed_date, 5535251881Speter changed_author, 5536251881Speter original_repos_relpath, 5537251881Speter original_repos_relpath ? repos_root_url 5538251881Speter : NULL, 5539251881Speter original_repos_relpath ? repos_uuid : NULL, 5540251881Speter copyfrom_rev, 5541251881Speter new_text_base_sha1_checksum, 5542251881Speter TRUE, 5543251881Speter new_props, 5544251881Speter FALSE /* is_move */, 5545251881Speter NULL /* conflict */, 5546251881Speter all_work_items, 5547251881Speter pool)); 5548251881Speter 5549251881Speter return svn_error_trace(svn_wc__wq_run(db, dir_abspath, 5550251881Speter cancel_func, cancel_baton, 5551251881Speter pool)); 5552251881Speter} 5553251881Speter 5554251881Spetersvn_error_t * 5555251881Spetersvn_wc__complete_directory_add(svn_wc_context_t *wc_ctx, 5556251881Speter const char *local_abspath, 5557251881Speter apr_hash_t *new_original_props, 5558251881Speter const char *copyfrom_url, 5559251881Speter svn_revnum_t copyfrom_rev, 5560251881Speter apr_pool_t *scratch_pool) 5561251881Speter{ 5562251881Speter svn_wc__db_status_t status; 5563251881Speter svn_node_kind_t kind; 5564251881Speter const char *original_repos_relpath; 5565251881Speter const char *original_root_url; 5566251881Speter const char *original_uuid; 5567251881Speter svn_boolean_t had_props; 5568251881Speter svn_boolean_t props_mod; 5569251881Speter 5570251881Speter svn_revnum_t original_revision; 5571251881Speter svn_revnum_t changed_rev; 5572251881Speter apr_time_t changed_date; 5573251881Speter const char *changed_author; 5574251881Speter 5575251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 5576251881Speter NULL, NULL, NULL, NULL, NULL, 5577251881Speter &original_repos_relpath, &original_root_url, 5578251881Speter &original_uuid, &original_revision, NULL, NULL, 5579251881Speter NULL, NULL, NULL, NULL, &had_props, &props_mod, 5580251881Speter NULL, NULL, NULL, 5581251881Speter wc_ctx->db, local_abspath, 5582251881Speter scratch_pool, scratch_pool)); 5583251881Speter 5584251881Speter if (status != svn_wc__db_status_added 5585251881Speter || kind != svn_node_dir 5586251881Speter || had_props 5587251881Speter || props_mod 5588251881Speter || !original_repos_relpath) 5589251881Speter { 5590251881Speter return svn_error_createf( 5591251881Speter SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 5592251881Speter _("'%s' is not an unmodified copied directory"), 5593251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 5594251881Speter } 5595251881Speter if (original_revision != copyfrom_rev 5596251881Speter || strcmp(copyfrom_url, 5597251881Speter svn_path_url_add_component2(original_root_url, 5598251881Speter original_repos_relpath, 5599251881Speter scratch_pool))) 5600251881Speter { 5601251881Speter return svn_error_createf( 5602251881Speter SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL, 5603251881Speter _("Copyfrom '%s' doesn't match original location of '%s'"), 5604251881Speter copyfrom_url, 5605251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 5606251881Speter } 5607251881Speter 5608251881Speter { 5609251881Speter apr_array_header_t *regular_props; 5610251881Speter apr_array_header_t *entry_props; 5611251881Speter 5612251881Speter SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props, 5613251881Speter scratch_pool), 5614251881Speter &entry_props, NULL, ®ular_props, 5615251881Speter scratch_pool)); 5616251881Speter 5617251881Speter /* Put regular props back into a hash table. */ 5618251881Speter new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool); 5619251881Speter 5620251881Speter /* Get the change_* info from the entry props. */ 5621251881Speter SVN_ERR(accumulate_last_change(&changed_rev, 5622251881Speter &changed_date, 5623251881Speter &changed_author, 5624251881Speter entry_props, scratch_pool, scratch_pool)); 5625251881Speter } 5626251881Speter 5627251881Speter return svn_error_trace( 5628251881Speter svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath, 5629251881Speter new_original_props, 5630251881Speter changed_rev, changed_date, changed_author, 5631251881Speter original_repos_relpath, original_root_url, 5632251881Speter original_uuid, original_revision, 5633251881Speter NULL /* children */, 5634286506Speter svn_depth_infinity, 5635251881Speter FALSE /* is_move */, 5636251881Speter NULL /* conflict */, 5637251881Speter NULL /* work_items */, 5638251881Speter scratch_pool)); 5639251881Speter} 5640