1251881Speter/* 2251881Speter * copy.c: wc 'copy' functionality. 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 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter 30251881Speter#include <string.h> 31251881Speter#include "svn_pools.h" 32251881Speter#include "svn_error.h" 33251881Speter#include "svn_dirent_uri.h" 34251881Speter#include "svn_path.h" 35251881Speter#include "svn_hash.h" 36251881Speter 37251881Speter#include "wc.h" 38251881Speter#include "workqueue.h" 39251881Speter#include "props.h" 40251881Speter#include "conflicts.h" 41251881Speter 42251881Speter#include "svn_private_config.h" 43251881Speter#include "private/svn_wc_private.h" 44251881Speter 45289180Speter/* #define RECORD_MIXED_MOVE */ 46251881Speter 47251881Speter/*** Code. ***/ 48251881Speter 49251881Speter/* Make a copy of the filesystem node (or tree if RECURSIVE) at 50251881Speter SRC_ABSPATH under a temporary name in the directory 51251881Speter TMPDIR_ABSPATH and return the absolute path of the copy in 52251881Speter *DST_ABSPATH. Return the node kind of SRC_ABSPATH in *KIND. If 53251881Speter SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate 54289180Speter that no copy was made. 55289180Speter 56289180Speter If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. 57289180Speter RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of 58289180Speter SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. 59289180Speter 60289180Speter These values will be used to avoid unneeded work. 61289180Speter */ 62251881Speterstatic svn_error_t * 63251881Spetercopy_to_tmpdir(svn_skel_t **work_item, 64251881Speter svn_node_kind_t *kind, 65251881Speter svn_wc__db_t *db, 66251881Speter const char *src_abspath, 67251881Speter const char *dst_abspath, 68251881Speter const char *tmpdir_abspath, 69251881Speter svn_boolean_t file_copy, 70251881Speter svn_boolean_t unversioned, 71289180Speter const svn_io_dirent2_t *dirent, 72289180Speter svn_filesize_t recorded_size, 73289180Speter apr_time_t recorded_time, 74251881Speter svn_cancel_func_t cancel_func, 75251881Speter void *cancel_baton, 76251881Speter apr_pool_t *result_pool, 77251881Speter apr_pool_t *scratch_pool) 78251881Speter{ 79251881Speter svn_boolean_t is_special; 80251881Speter svn_io_file_del_t delete_when; 81251881Speter const char *dst_tmp_abspath; 82251881Speter svn_node_kind_t dsk_kind; 83251881Speter if (!kind) 84251881Speter kind = &dsk_kind; 85251881Speter 86251881Speter *work_item = NULL; 87251881Speter 88289180Speter if (dirent) 89289180Speter { 90289180Speter *kind = dirent->kind; 91289180Speter is_special = dirent->special; 92289180Speter } 93289180Speter else 94289180Speter SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, 95289180Speter scratch_pool)); 96251881Speter if (*kind == svn_node_none) 97251881Speter { 98251881Speter return SVN_NO_ERROR; 99251881Speter } 100251881Speter else if (*kind == svn_node_unknown) 101251881Speter { 102251881Speter return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 103251881Speter _("Source '%s' is unexpected kind"), 104251881Speter svn_dirent_local_style(src_abspath, 105251881Speter scratch_pool)); 106251881Speter } 107251881Speter else if (*kind == svn_node_dir || is_special) 108251881Speter delete_when = svn_io_file_del_on_close; 109251881Speter else /* the default case: (*kind == svn_node_file) */ 110251881Speter delete_when = svn_io_file_del_none; 111251881Speter 112251881Speter /* ### Do we need a pool cleanup to remove the copy? We can't use 113251881Speter ### svn_io_file_del_on_pool_cleanup above because a) it won't 114251881Speter ### handle the directory case and b) we need to be able to remove 115251881Speter ### the cleanup before queueing the move work item. */ 116251881Speter 117251881Speter if (file_copy && !unversioned) 118251881Speter { 119251881Speter svn_boolean_t modified; 120251881Speter /* It's faster to look for mods on the source now, as 121251881Speter the timestamp might match, than to examine the 122251881Speter destination later as the destination timestamp will 123251881Speter never match. */ 124289180Speter 125289180Speter if (dirent 126289180Speter && dirent->kind == svn_node_file 127289180Speter && recorded_size != SVN_INVALID_FILESIZE 128289180Speter && recorded_size == dirent->filesize 129289180Speter && recorded_time == dirent->mtime) 130289180Speter { 131289180Speter modified = FALSE; /* Recorded matches on-disk. Easy out */ 132289180Speter } 133289180Speter else 134289180Speter { 135289180Speter SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, src_abspath, 136289180Speter FALSE, scratch_pool)); 137289180Speter } 138289180Speter 139251881Speter if (!modified) 140251881Speter { 141251881Speter /* Why create a temp copy if we can just reinstall from pristine? */ 142251881Speter SVN_ERR(svn_wc__wq_build_file_install(work_item, 143251881Speter db, dst_abspath, NULL, FALSE, 144251881Speter TRUE, 145251881Speter result_pool, scratch_pool)); 146251881Speter return SVN_NO_ERROR; 147251881Speter } 148251881Speter } 149289180Speter else if (*kind == svn_node_dir && !file_copy) 150289180Speter { 151289180Speter /* Just build a new direcory from the workqueue */ 152289180Speter SVN_ERR(svn_wc__wq_build_dir_install(work_item, 153289180Speter db, dst_abspath, 154289180Speter result_pool, scratch_pool)); 155251881Speter 156289180Speter return SVN_NO_ERROR; 157289180Speter } 158289180Speter 159251881Speter /* Set DST_TMP_ABSPATH to a temporary unique path. If *KIND is file, leave 160251881Speter a file there and then overwrite it; otherwise leave no node on disk at 161251881Speter that path. In the latter case, something else might use that path 162251881Speter before we get around to using it a moment later, but never mind. */ 163251881Speter SVN_ERR(svn_io_open_unique_file3(NULL, &dst_tmp_abspath, tmpdir_abspath, 164251881Speter delete_when, scratch_pool, scratch_pool)); 165251881Speter 166251881Speter if (*kind == svn_node_dir) 167251881Speter { 168251881Speter if (file_copy) 169251881Speter SVN_ERR(svn_io_copy_dir_recursively( 170251881Speter src_abspath, 171251881Speter tmpdir_abspath, 172251881Speter svn_dirent_basename(dst_tmp_abspath, scratch_pool), 173251881Speter TRUE, /* copy_perms */ 174251881Speter cancel_func, cancel_baton, 175251881Speter scratch_pool)); 176251881Speter else 177251881Speter SVN_ERR(svn_io_dir_make(dst_tmp_abspath, APR_OS_DEFAULT, scratch_pool)); 178251881Speter } 179251881Speter else if (!is_special) 180251881Speter SVN_ERR(svn_io_copy_file(src_abspath, dst_tmp_abspath, 181251881Speter TRUE /* copy_perms */, 182251881Speter scratch_pool)); 183251881Speter else 184251881Speter SVN_ERR(svn_io_copy_link(src_abspath, dst_tmp_abspath, scratch_pool)); 185251881Speter 186251881Speter if (file_copy) 187251881Speter { 188251881Speter /* Remove 'read-only' from the destination file; it's a local add now. */ 189251881Speter SVN_ERR(svn_io_set_file_read_write(dst_tmp_abspath, 190251881Speter FALSE, scratch_pool)); 191251881Speter } 192251881Speter 193251881Speter SVN_ERR(svn_wc__wq_build_file_move(work_item, db, dst_abspath, 194251881Speter dst_tmp_abspath, dst_abspath, 195251881Speter result_pool, scratch_pool)); 196251881Speter 197251881Speter return SVN_NO_ERROR; 198251881Speter} 199251881Speter 200251881Speter/* Copy the versioned file SRC_ABSPATH in DB to the path DST_ABSPATH in DB. 201251881Speter If METADATA_ONLY is true, copy only the versioned metadata, 202251881Speter otherwise copy both the versioned metadata and the filesystem node (even 203251881Speter if it is the wrong kind, and recursively if it is a dir). 204251881Speter 205251881Speter If IS_MOVE is true, record move information in working copy meta 206251881Speter data in addition to copying the file. 207251881Speter 208251881Speter If the versioned file has a text conflict, and the .mine file exists in 209251881Speter the filesystem, copy the .mine file to DST_ABSPATH. Otherwise, copy the 210251881Speter versioned file itself. 211251881Speter 212251881Speter This also works for versioned symlinks that are stored in the db as 213289180Speter svn_node_file with svn:special set. 214289180Speter 215289180Speter If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. 216289180Speter RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of 217289180Speter SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. 218289180Speter 219289180Speter These values will be used to avoid unneeded work. 220289180Speter*/ 221251881Speterstatic svn_error_t * 222251881Spetercopy_versioned_file(svn_wc__db_t *db, 223251881Speter const char *src_abspath, 224251881Speter const char *dst_abspath, 225251881Speter const char *dst_op_root_abspath, 226251881Speter const char *tmpdir_abspath, 227251881Speter svn_boolean_t metadata_only, 228251881Speter svn_boolean_t conflicted, 229251881Speter svn_boolean_t is_move, 230289180Speter const svn_io_dirent2_t *dirent, 231289180Speter svn_filesize_t recorded_size, 232289180Speter apr_time_t recorded_time, 233251881Speter svn_cancel_func_t cancel_func, 234251881Speter void *cancel_baton, 235251881Speter svn_wc_notify_func2_t notify_func, 236251881Speter void *notify_baton, 237251881Speter apr_pool_t *scratch_pool) 238251881Speter{ 239251881Speter svn_skel_t *work_items = NULL; 240251881Speter 241251881Speter /* In case we are copying from one WC to another (e.g. an external dir), 242251881Speter ensure the destination WC has a copy of the pristine text. */ 243251881Speter 244251881Speter /* Prepare a temp copy of the filesystem node. It is usually a file, but 245251881Speter copy recursively if it's a dir. */ 246251881Speter if (!metadata_only) 247251881Speter { 248251881Speter const char *my_src_abspath = NULL; 249251881Speter svn_boolean_t handle_as_unversioned = FALSE; 250251881Speter 251251881Speter /* By default, take the copy source as given. */ 252251881Speter my_src_abspath = src_abspath; 253251881Speter 254251881Speter if (conflicted) 255251881Speter { 256251881Speter svn_skel_t *conflict; 257251881Speter const char *conflict_working; 258251881Speter svn_error_t *err; 259251881Speter 260251881Speter /* Is there a text conflict at the source path? */ 261289180Speter SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, 262289180Speter db, src_abspath, 263289180Speter scratch_pool, scratch_pool)); 264251881Speter 265251881Speter err = svn_wc__conflict_read_text_conflict(&conflict_working, NULL, NULL, 266251881Speter db, src_abspath, conflict, 267251881Speter scratch_pool, 268251881Speter scratch_pool); 269251881Speter 270251881Speter if (err && err->apr_err == SVN_ERR_WC_MISSING) 271251881Speter { 272251881Speter /* not text conflicted */ 273251881Speter svn_error_clear(err); 274251881Speter conflict_working = NULL; 275251881Speter } 276251881Speter else 277251881Speter SVN_ERR(err); 278251881Speter 279251881Speter if (conflict_working) 280251881Speter { 281251881Speter svn_node_kind_t working_kind; 282251881Speter 283251881Speter /* Does the ".mine" file exist? */ 284251881Speter SVN_ERR(svn_io_check_path(conflict_working, &working_kind, 285251881Speter scratch_pool)); 286251881Speter 287251881Speter if (working_kind == svn_node_file) 288251881Speter { 289251881Speter /* Don't perform unmodified/pristine optimization */ 290251881Speter handle_as_unversioned = TRUE; 291251881Speter my_src_abspath = conflict_working; 292251881Speter } 293251881Speter } 294251881Speter } 295251881Speter 296251881Speter SVN_ERR(copy_to_tmpdir(&work_items, NULL, db, my_src_abspath, 297251881Speter dst_abspath, tmpdir_abspath, 298251881Speter TRUE /* file_copy */, 299251881Speter handle_as_unversioned /* unversioned */, 300289180Speter dirent, recorded_size, recorded_time, 301251881Speter cancel_func, cancel_baton, 302251881Speter scratch_pool, scratch_pool)); 303251881Speter } 304251881Speter 305251881Speter /* Copy the (single) node's metadata, and move the new filesystem node 306251881Speter into place. */ 307251881Speter SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath, 308251881Speter dst_op_root_abspath, is_move, work_items, 309251881Speter scratch_pool)); 310251881Speter 311251881Speter if (notify_func) 312251881Speter { 313251881Speter svn_wc_notify_t *notify 314251881Speter = svn_wc_create_notify(dst_abspath, svn_wc_notify_add, 315251881Speter scratch_pool); 316251881Speter notify->kind = svn_node_file; 317251881Speter 318251881Speter (*notify_func)(notify_baton, notify, scratch_pool); 319251881Speter } 320251881Speter return SVN_NO_ERROR; 321251881Speter} 322251881Speter 323251881Speter/* Copy the versioned dir SRC_ABSPATH in DB to the path DST_ABSPATH in DB, 324251881Speter recursively. If METADATA_ONLY is true, copy only the versioned metadata, 325251881Speter otherwise copy both the versioned metadata and the filesystem nodes (even 326251881Speter if they are the wrong kind, and including unversioned children). 327251881Speter If IS_MOVE is true, record move information in working copy meta 328251881Speter data in addition to copying the directory. 329251881Speter 330251881Speter WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root) 331289180Speter 332289180Speter If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. 333251881Speter */ 334251881Speterstatic svn_error_t * 335251881Spetercopy_versioned_dir(svn_wc__db_t *db, 336251881Speter const char *src_abspath, 337251881Speter const char *dst_abspath, 338251881Speter const char *dst_op_root_abspath, 339251881Speter const char *tmpdir_abspath, 340251881Speter svn_boolean_t metadata_only, 341251881Speter svn_boolean_t is_move, 342289180Speter const svn_io_dirent2_t *dirent, 343251881Speter svn_cancel_func_t cancel_func, 344251881Speter void *cancel_baton, 345251881Speter svn_wc_notify_func2_t notify_func, 346251881Speter void *notify_baton, 347251881Speter apr_pool_t *scratch_pool) 348251881Speter{ 349251881Speter svn_skel_t *work_items = NULL; 350251881Speter const char *dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool); 351251881Speter apr_hash_t *versioned_children; 352251881Speter apr_hash_t *conflicted_children; 353251881Speter apr_hash_t *disk_children; 354251881Speter apr_hash_index_t *hi; 355251881Speter svn_node_kind_t disk_kind; 356251881Speter apr_pool_t *iterpool; 357251881Speter 358251881Speter /* Prepare a temp copy of the single filesystem node (usually a dir). */ 359251881Speter if (!metadata_only) 360251881Speter { 361251881Speter SVN_ERR(copy_to_tmpdir(&work_items, &disk_kind, 362251881Speter db, src_abspath, dst_abspath, 363251881Speter tmpdir_abspath, 364251881Speter FALSE /* file_copy */, 365251881Speter FALSE /* unversioned */, 366289180Speter dirent, SVN_INVALID_FILESIZE, 0, 367251881Speter cancel_func, cancel_baton, 368251881Speter scratch_pool, scratch_pool)); 369251881Speter } 370251881Speter 371251881Speter /* Copy the (single) node's metadata, and move the new filesystem node 372251881Speter into place. */ 373251881Speter SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath, 374251881Speter dst_op_root_abspath, is_move, work_items, 375251881Speter scratch_pool)); 376251881Speter 377251881Speter if (notify_func) 378251881Speter { 379251881Speter svn_wc_notify_t *notify 380251881Speter = svn_wc_create_notify(dst_abspath, svn_wc_notify_add, 381251881Speter scratch_pool); 382251881Speter notify->kind = svn_node_dir; 383251881Speter 384251881Speter /* When we notify that we performed a copy, make sure we already did */ 385251881Speter if (work_items != NULL) 386251881Speter SVN_ERR(svn_wc__wq_run(db, dir_abspath, 387251881Speter cancel_func, cancel_baton, scratch_pool)); 388251881Speter 389251881Speter (*notify_func)(notify_baton, notify, scratch_pool); 390251881Speter } 391251881Speter 392251881Speter if (!metadata_only && disk_kind == svn_node_dir) 393251881Speter /* All filesystem children, versioned and unversioned. We're only 394251881Speter interested in their names, so we can pass TRUE as the only_check_type 395251881Speter param. */ 396251881Speter SVN_ERR(svn_io_get_dirents3(&disk_children, src_abspath, TRUE, 397251881Speter scratch_pool, scratch_pool)); 398251881Speter else 399251881Speter disk_children = NULL; 400251881Speter 401251881Speter /* Copy all the versioned children */ 402251881Speter iterpool = svn_pool_create(scratch_pool); 403251881Speter SVN_ERR(svn_wc__db_read_children_info(&versioned_children, 404251881Speter &conflicted_children, 405251881Speter db, src_abspath, 406289180Speter FALSE /* base_tree_only */, 407251881Speter scratch_pool, iterpool)); 408251881Speter for (hi = apr_hash_first(scratch_pool, versioned_children); 409251881Speter hi; 410251881Speter hi = apr_hash_next(hi)) 411251881Speter { 412251881Speter const char *child_name, *child_src_abspath, *child_dst_abspath; 413251881Speter struct svn_wc__db_info_t *info; 414251881Speter 415251881Speter svn_pool_clear(iterpool); 416251881Speter 417251881Speter if (cancel_func) 418251881Speter SVN_ERR(cancel_func(cancel_baton)); 419251881Speter 420289180Speter child_name = apr_hash_this_key(hi); 421289180Speter info = apr_hash_this_val(hi); 422251881Speter child_src_abspath = svn_dirent_join(src_abspath, child_name, iterpool); 423251881Speter child_dst_abspath = svn_dirent_join(dst_abspath, child_name, iterpool); 424251881Speter 425251881Speter if (info->op_root) 426251881Speter SVN_ERR(svn_wc__db_op_copy_shadowed_layer(db, 427251881Speter child_src_abspath, 428251881Speter child_dst_abspath, 429251881Speter is_move, 430251881Speter scratch_pool)); 431251881Speter 432251881Speter if (info->status == svn_wc__db_status_normal 433251881Speter || info->status == svn_wc__db_status_added) 434251881Speter { 435251881Speter /* We have more work to do than just changing the DB */ 436251881Speter if (info->kind == svn_node_file) 437251881Speter { 438251881Speter /* We should skip this node if this child is a file external 439251881Speter (issues #3589, #4000) */ 440251881Speter if (!info->file_external) 441251881Speter SVN_ERR(copy_versioned_file(db, 442251881Speter child_src_abspath, 443251881Speter child_dst_abspath, 444251881Speter dst_op_root_abspath, 445251881Speter tmpdir_abspath, 446251881Speter metadata_only, info->conflicted, 447251881Speter is_move, 448289180Speter disk_children 449289180Speter ? svn_hash_gets(disk_children, 450289180Speter child_name) 451289180Speter : NULL, 452289180Speter info->recorded_size, 453289180Speter info->recorded_time, 454251881Speter cancel_func, cancel_baton, 455251881Speter NULL, NULL, 456251881Speter iterpool)); 457251881Speter } 458251881Speter else if (info->kind == svn_node_dir) 459251881Speter SVN_ERR(copy_versioned_dir(db, 460251881Speter child_src_abspath, child_dst_abspath, 461251881Speter dst_op_root_abspath, tmpdir_abspath, 462251881Speter metadata_only, is_move, 463289180Speter disk_children 464289180Speter ? svn_hash_gets(disk_children, 465289180Speter child_name) 466289180Speter : NULL, 467251881Speter cancel_func, cancel_baton, NULL, NULL, 468251881Speter iterpool)); 469251881Speter else 470251881Speter return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 471251881Speter _("cannot handle node kind for '%s'"), 472251881Speter svn_dirent_local_style(child_src_abspath, 473251881Speter scratch_pool)); 474251881Speter } 475251881Speter else if (info->status == svn_wc__db_status_deleted 476251881Speter || info->status == svn_wc__db_status_not_present 477251881Speter || info->status == svn_wc__db_status_excluded) 478251881Speter { 479251881Speter /* This will be copied as some kind of deletion. Don't touch 480251881Speter any actual files */ 481251881Speter SVN_ERR(svn_wc__db_op_copy(db, child_src_abspath, 482251881Speter child_dst_abspath, dst_op_root_abspath, 483251881Speter is_move, NULL, iterpool)); 484251881Speter 485289180Speter /* Don't recurse on children when all we do is creating not-present 486251881Speter children */ 487251881Speter } 488251881Speter else if (info->status == svn_wc__db_status_incomplete) 489251881Speter { 490251881Speter /* Should go ahead and copy incomplete to incomplete? Try to 491251881Speter copy as much as possible, or give up early? */ 492251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 493251881Speter _("Cannot handle status of '%s'"), 494251881Speter svn_dirent_local_style(child_src_abspath, 495251881Speter iterpool)); 496251881Speter } 497251881Speter else 498251881Speter { 499251881Speter SVN_ERR_ASSERT(info->status == svn_wc__db_status_server_excluded); 500251881Speter 501251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 502251881Speter _("Cannot copy '%s' excluded by server"), 503251881Speter svn_dirent_local_style(child_src_abspath, 504251881Speter iterpool)); 505251881Speter } 506251881Speter 507251881Speter if (disk_children 508251881Speter && (info->status == svn_wc__db_status_normal 509251881Speter || info->status == svn_wc__db_status_added)) 510251881Speter { 511251881Speter /* Remove versioned child as it has been handled */ 512251881Speter svn_hash_sets(disk_children, child_name, NULL); 513251881Speter } 514251881Speter } 515251881Speter 516251881Speter /* Copy the remaining filesystem children, which are unversioned, skipping 517251881Speter any conflict-marker files. */ 518251881Speter if (disk_children && apr_hash_count(disk_children)) 519251881Speter { 520251881Speter apr_hash_t *marker_files; 521251881Speter 522251881Speter SVN_ERR(svn_wc__db_get_conflict_marker_files(&marker_files, db, 523251881Speter src_abspath, scratch_pool, 524251881Speter scratch_pool)); 525251881Speter 526251881Speter work_items = NULL; 527251881Speter 528251881Speter for (hi = apr_hash_first(scratch_pool, disk_children); hi; 529251881Speter hi = apr_hash_next(hi)) 530251881Speter { 531289180Speter const char *name = apr_hash_this_key(hi); 532251881Speter const char *unver_src_abspath, *unver_dst_abspath; 533251881Speter svn_skel_t *work_item; 534251881Speter 535251881Speter if (svn_wc_is_adm_dir(name, iterpool)) 536251881Speter continue; 537251881Speter 538251881Speter if (cancel_func) 539251881Speter SVN_ERR(cancel_func(cancel_baton)); 540251881Speter 541251881Speter svn_pool_clear(iterpool); 542251881Speter unver_src_abspath = svn_dirent_join(src_abspath, name, iterpool); 543251881Speter unver_dst_abspath = svn_dirent_join(dst_abspath, name, iterpool); 544251881Speter 545251881Speter if (marker_files && 546251881Speter svn_hash_gets(marker_files, unver_src_abspath)) 547251881Speter continue; 548251881Speter 549251881Speter SVN_ERR(copy_to_tmpdir(&work_item, NULL, db, unver_src_abspath, 550251881Speter unver_dst_abspath, tmpdir_abspath, 551251881Speter TRUE /* recursive */, TRUE /* unversioned */, 552289180Speter NULL, SVN_INVALID_FILESIZE, 0, 553251881Speter cancel_func, cancel_baton, 554251881Speter scratch_pool, iterpool)); 555251881Speter 556251881Speter if (work_item) 557251881Speter work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 558251881Speter } 559251881Speter SVN_ERR(svn_wc__db_wq_add(db, dst_abspath, work_items, iterpool)); 560251881Speter } 561251881Speter 562251881Speter svn_pool_destroy(iterpool); 563251881Speter 564251881Speter return SVN_NO_ERROR; 565251881Speter} 566251881Speter 567251881Speter 568251881Speter/* The guts of svn_wc_copy3() and svn_wc_move(). 569251881Speter * The additional parameter IS_MOVE indicates whether this is a copy or 570251881Speter * a move operation. 571251881Speter * 572289180Speter * If RECORD_MOVE_ON_DELETE is not NULL and a move had to be degraded 573289180Speter * to a copy, then set *RECORD_MOVE_ON_DELETE to FALSE. */ 574251881Speterstatic svn_error_t * 575289180Spetercopy_or_move(svn_boolean_t *record_move_on_delete, 576251881Speter svn_wc_context_t *wc_ctx, 577251881Speter const char *src_abspath, 578251881Speter const char *dst_abspath, 579251881Speter svn_boolean_t metadata_only, 580251881Speter svn_boolean_t is_move, 581251881Speter svn_boolean_t allow_mixed_revisions, 582251881Speter svn_cancel_func_t cancel_func, 583251881Speter void *cancel_baton, 584251881Speter svn_wc_notify_func2_t notify_func, 585251881Speter void *notify_baton, 586251881Speter apr_pool_t *scratch_pool) 587251881Speter{ 588251881Speter svn_wc__db_t *db = wc_ctx->db; 589251881Speter svn_node_kind_t src_db_kind; 590251881Speter const char *dstdir_abspath; 591251881Speter svn_boolean_t conflicted; 592251881Speter const char *tmpdir_abspath; 593251881Speter const char *src_wcroot_abspath; 594251881Speter const char *dst_wcroot_abspath; 595251881Speter svn_boolean_t within_one_wc; 596251881Speter svn_wc__db_status_t src_status; 597251881Speter svn_error_t *err; 598289180Speter svn_filesize_t recorded_size; 599289180Speter apr_time_t recorded_time; 600251881Speter 601251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath)); 602251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); 603251881Speter 604251881Speter dstdir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool); 605251881Speter 606251881Speter /* Ensure DSTDIR_ABSPATH belongs to the same repository as SRC_ABSPATH; 607251881Speter throw an error if not. */ 608251881Speter { 609251881Speter svn_wc__db_status_t dstdir_status; 610251881Speter const char *src_repos_root_url, *dst_repos_root_url; 611251881Speter const char *src_repos_uuid, *dst_repos_uuid; 612251881Speter const char *src_repos_relpath; 613251881Speter 614251881Speter err = svn_wc__db_read_info(&src_status, &src_db_kind, NULL, 615251881Speter &src_repos_relpath, &src_repos_root_url, 616251881Speter &src_repos_uuid, NULL, NULL, NULL, NULL, NULL, 617289180Speter NULL, NULL, NULL, NULL, NULL, NULL, 618289180Speter &recorded_size, &recorded_time, 619251881Speter NULL, &conflicted, NULL, NULL, NULL, NULL, 620251881Speter NULL, NULL, 621251881Speter db, src_abspath, scratch_pool, scratch_pool); 622251881Speter 623251881Speter if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 624251881Speter { 625251881Speter /* Replicate old error code and text */ 626251881Speter svn_error_clear(err); 627251881Speter return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 628251881Speter _("'%s' is not under version control"), 629251881Speter svn_dirent_local_style(src_abspath, 630251881Speter scratch_pool)); 631251881Speter } 632251881Speter else 633251881Speter SVN_ERR(err); 634251881Speter 635251881Speter /* Do this now, as we know the right data is cached */ 636251881Speter SVN_ERR(svn_wc__db_get_wcroot(&src_wcroot_abspath, db, src_abspath, 637251881Speter scratch_pool, scratch_pool)); 638251881Speter 639251881Speter switch (src_status) 640251881Speter { 641251881Speter case svn_wc__db_status_deleted: 642251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 643251881Speter _("Deleted node '%s' can't be copied."), 644251881Speter svn_dirent_local_style(src_abspath, 645251881Speter scratch_pool)); 646251881Speter 647251881Speter case svn_wc__db_status_excluded: 648251881Speter case svn_wc__db_status_server_excluded: 649251881Speter case svn_wc__db_status_not_present: 650251881Speter return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 651251881Speter _("The node '%s' was not found."), 652251881Speter svn_dirent_local_style(src_abspath, 653251881Speter scratch_pool)); 654251881Speter default: 655251881Speter break; 656251881Speter } 657251881Speter 658251881Speter if (is_move && ! strcmp(src_abspath, src_wcroot_abspath)) 659251881Speter { 660251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 661251881Speter _("'%s' is the root of a working copy and " 662251881Speter "cannot be moved"), 663251881Speter svn_dirent_local_style(src_abspath, 664251881Speter scratch_pool)); 665251881Speter } 666251881Speter if (is_move && src_repos_relpath && !src_repos_relpath[0]) 667251881Speter { 668251881Speter return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 669251881Speter _("'%s' represents the repository root " 670251881Speter "and cannot be moved"), 671251881Speter svn_dirent_local_style(src_abspath, 672251881Speter scratch_pool)); 673251881Speter } 674251881Speter 675251881Speter err = svn_wc__db_read_info(&dstdir_status, NULL, NULL, NULL, 676251881Speter &dst_repos_root_url, &dst_repos_uuid, NULL, 677251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 678251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 679251881Speter NULL, NULL, NULL, NULL, 680251881Speter NULL, NULL, NULL, 681251881Speter db, dstdir_abspath, 682251881Speter scratch_pool, scratch_pool); 683251881Speter 684251881Speter if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 685251881Speter { 686251881Speter /* An unversioned destination directory exists on disk. */ 687251881Speter svn_error_clear(err); 688251881Speter return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 689251881Speter _("'%s' is not under version control"), 690251881Speter svn_dirent_local_style(dstdir_abspath, 691251881Speter scratch_pool)); 692251881Speter } 693251881Speter else 694251881Speter SVN_ERR(err); 695251881Speter 696251881Speter /* Do this now, as we know the right data is cached */ 697251881Speter SVN_ERR(svn_wc__db_get_wcroot(&dst_wcroot_abspath, db, dstdir_abspath, 698251881Speter scratch_pool, scratch_pool)); 699251881Speter 700251881Speter if (!src_repos_root_url) 701251881Speter { 702251881Speter if (src_status == svn_wc__db_status_added) 703251881Speter SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, 704251881Speter &src_repos_root_url, 705251881Speter &src_repos_uuid, NULL, NULL, NULL, 706251881Speter NULL, 707251881Speter db, src_abspath, 708251881Speter scratch_pool, scratch_pool)); 709251881Speter else 710251881Speter /* If not added, the node must have a base or we can't copy */ 711289180Speter SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, 712289180Speter &src_repos_root_url, 713289180Speter &src_repos_uuid, NULL, NULL, NULL, 714289180Speter NULL, NULL, NULL, NULL, NULL, NULL, 715289180Speter NULL, 716289180Speter db, src_abspath, 717289180Speter scratch_pool, scratch_pool)); 718251881Speter } 719251881Speter 720251881Speter if (!dst_repos_root_url) 721251881Speter { 722251881Speter if (dstdir_status == svn_wc__db_status_added) 723251881Speter SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL, 724251881Speter &dst_repos_root_url, 725251881Speter &dst_repos_uuid, NULL, NULL, NULL, 726251881Speter NULL, 727251881Speter db, dstdir_abspath, 728251881Speter scratch_pool, scratch_pool)); 729251881Speter else 730251881Speter /* If not added, the node must have a base or we can't copy */ 731289180Speter SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, 732289180Speter &dst_repos_root_url, 733289180Speter &dst_repos_uuid, NULL, NULL, NULL, 734289180Speter NULL, NULL, NULL, NULL, NULL, NULL, 735289180Speter NULL, 736289180Speter db, dstdir_abspath, 737289180Speter scratch_pool, scratch_pool)); 738251881Speter } 739251881Speter 740251881Speter if (strcmp(src_repos_root_url, dst_repos_root_url) != 0 741251881Speter || strcmp(src_repos_uuid, dst_repos_uuid) != 0) 742251881Speter return svn_error_createf( 743251881Speter SVN_ERR_WC_INVALID_SCHEDULE, NULL, 744251881Speter _("Cannot copy to '%s', as it is not from repository '%s'; " 745251881Speter "it is from '%s'"), 746251881Speter svn_dirent_local_style(dst_abspath, scratch_pool), 747251881Speter src_repos_root_url, dst_repos_root_url); 748251881Speter 749251881Speter if (dstdir_status == svn_wc__db_status_deleted) 750251881Speter return svn_error_createf( 751251881Speter SVN_ERR_WC_INVALID_SCHEDULE, NULL, 752251881Speter _("Cannot copy to '%s' as it is scheduled for deletion"), 753251881Speter svn_dirent_local_style(dst_abspath, scratch_pool)); 754251881Speter /* ### should report dstdir_abspath instead of dst_abspath? */ 755251881Speter } 756251881Speter 757251881Speter /* TODO(#2843): Rework the error report. */ 758251881Speter /* Check if the copy target is missing or hidden and thus not exist on the 759251881Speter disk, before actually doing the file copy. */ 760251881Speter { 761251881Speter svn_wc__db_status_t dst_status; 762251881Speter 763251881Speter err = svn_wc__db_read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, 764251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 765251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 766251881Speter NULL, NULL, NULL, NULL, NULL, 767251881Speter db, dst_abspath, scratch_pool, scratch_pool); 768251881Speter 769251881Speter if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 770251881Speter return svn_error_trace(err); 771251881Speter 772251881Speter svn_error_clear(err); 773251881Speter 774251881Speter if (!err) 775251881Speter switch (dst_status) 776251881Speter { 777251881Speter case svn_wc__db_status_excluded: 778251881Speter return svn_error_createf( 779251881Speter SVN_ERR_ENTRY_EXISTS, NULL, 780251881Speter _("'%s' is already under version control " 781251881Speter "but is excluded."), 782251881Speter svn_dirent_local_style(dst_abspath, scratch_pool)); 783251881Speter case svn_wc__db_status_server_excluded: 784251881Speter return svn_error_createf( 785251881Speter SVN_ERR_ENTRY_EXISTS, NULL, 786251881Speter _("'%s' is already under version control"), 787251881Speter svn_dirent_local_style(dst_abspath, scratch_pool)); 788251881Speter 789251881Speter case svn_wc__db_status_deleted: 790251881Speter case svn_wc__db_status_not_present: 791251881Speter break; /* OK to add */ 792251881Speter 793251881Speter default: 794362181Sdim if (!metadata_only) 795362181Sdim return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, 796362181Sdim _("There is already a versioned item '%s'"), 797362181Sdim svn_dirent_local_style(dst_abspath, 798362181Sdim scratch_pool)); 799251881Speter } 800251881Speter } 801251881Speter 802251881Speter /* Check that the target path is not obstructed, if required. */ 803251881Speter if (!metadata_only) 804251881Speter { 805251881Speter svn_node_kind_t dst_kind; 806251881Speter 807251881Speter /* (We need only to check the root of the copy, not every path inside 808251881Speter copy_versioned_file/_dir.) */ 809251881Speter SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind, scratch_pool)); 810251881Speter if (dst_kind != svn_node_none) 811251881Speter return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL, 812251881Speter _("'%s' already exists and is in the way"), 813251881Speter svn_dirent_local_style(dst_abspath, 814251881Speter scratch_pool)); 815251881Speter } 816251881Speter 817251881Speter SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db, 818251881Speter dstdir_abspath, 819251881Speter scratch_pool, scratch_pool)); 820251881Speter 821251881Speter within_one_wc = (strcmp(src_wcroot_abspath, dst_wcroot_abspath) == 0); 822251881Speter 823251881Speter if (is_move 824251881Speter && !within_one_wc) 825251881Speter { 826289180Speter if (record_move_on_delete) 827289180Speter *record_move_on_delete = FALSE; 828251881Speter 829251881Speter is_move = FALSE; 830251881Speter } 831251881Speter 832251881Speter if (!within_one_wc) 833251881Speter SVN_ERR(svn_wc__db_pristine_transfer(db, src_abspath, dst_wcroot_abspath, 834251881Speter cancel_func, cancel_baton, 835251881Speter scratch_pool)); 836251881Speter 837251881Speter if (src_db_kind == svn_node_file 838251881Speter || src_db_kind == svn_node_symlink) 839251881Speter { 840251881Speter err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath, 841251881Speter tmpdir_abspath, 842251881Speter metadata_only, conflicted, is_move, 843289180Speter NULL, recorded_size, recorded_time, 844251881Speter cancel_func, cancel_baton, 845251881Speter notify_func, notify_baton, 846251881Speter scratch_pool); 847251881Speter } 848251881Speter else 849251881Speter { 850251881Speter if (is_move 851251881Speter && src_status == svn_wc__db_status_normal) 852251881Speter { 853251881Speter svn_revnum_t min_rev; 854251881Speter svn_revnum_t max_rev; 855251881Speter 856251881Speter /* Verify that the move source is a single-revision subtree. */ 857251881Speter SVN_ERR(svn_wc__db_min_max_revisions(&min_rev, &max_rev, db, 858251881Speter src_abspath, FALSE, scratch_pool)); 859251881Speter if (SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev) && 860251881Speter min_rev != max_rev) 861251881Speter { 862251881Speter if (!allow_mixed_revisions) 863251881Speter return svn_error_createf(SVN_ERR_WC_MIXED_REVISIONS, NULL, 864251881Speter _("Cannot move mixed-revision " 865251881Speter "subtree '%s' [%ld:%ld]; " 866251881Speter "try updating it first"), 867251881Speter svn_dirent_local_style(src_abspath, 868251881Speter scratch_pool), 869251881Speter min_rev, max_rev); 870251881Speter 871289180Speter#ifndef RECORD_MIXED_MOVE 872251881Speter is_move = FALSE; 873289180Speter if (record_move_on_delete) 874289180Speter *record_move_on_delete = FALSE; 875289180Speter#endif 876251881Speter } 877251881Speter } 878251881Speter 879251881Speter err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath, 880251881Speter tmpdir_abspath, metadata_only, is_move, 881289180Speter NULL /* dirent */, 882251881Speter cancel_func, cancel_baton, 883251881Speter notify_func, notify_baton, 884251881Speter scratch_pool); 885251881Speter } 886251881Speter 887251881Speter if (err && svn_error_find_cause(err, SVN_ERR_CANCELLED)) 888251881Speter return svn_error_trace(err); 889251881Speter 890251881Speter if (is_move) 891251881Speter err = svn_error_compose_create(err, 892251881Speter svn_wc__db_op_handle_move_back(NULL, 893251881Speter db, dst_abspath, src_abspath, 894251881Speter NULL /* work_items */, 895251881Speter scratch_pool)); 896251881Speter 897251881Speter /* Run the work queue with the remaining work */ 898251881Speter SVN_ERR(svn_error_compose_create( 899251881Speter err, 900251881Speter svn_wc__wq_run(db, dst_abspath, 901251881Speter cancel_func, cancel_baton, 902251881Speter scratch_pool))); 903251881Speter 904251881Speter return SVN_NO_ERROR; 905251881Speter} 906251881Speter 907251881Speter 908251881Speter/* Public Interface */ 909251881Speter 910251881Spetersvn_error_t * 911251881Spetersvn_wc_copy3(svn_wc_context_t *wc_ctx, 912251881Speter const char *src_abspath, 913251881Speter const char *dst_abspath, 914251881Speter svn_boolean_t metadata_only, 915251881Speter svn_cancel_func_t cancel_func, 916251881Speter void *cancel_baton, 917251881Speter svn_wc_notify_func2_t notify_func, 918251881Speter void *notify_baton, 919251881Speter apr_pool_t *scratch_pool) 920251881Speter{ 921251881Speter /* Verify that we have the required write lock. */ 922251881Speter SVN_ERR(svn_wc__write_check(wc_ctx->db, 923251881Speter svn_dirent_dirname(dst_abspath, scratch_pool), 924251881Speter scratch_pool)); 925251881Speter 926251881Speter return svn_error_trace(copy_or_move(NULL, wc_ctx, src_abspath, dst_abspath, 927251881Speter metadata_only, FALSE /* is_move */, 928251881Speter TRUE /* allow_mixed_revisions */, 929251881Speter cancel_func, cancel_baton, 930251881Speter notify_func, notify_baton, 931251881Speter scratch_pool)); 932251881Speter} 933251881Speter 934251881Speter 935251881Speter/* Remove the conflict markers of NODE_ABSPATH, that were left over after 936251881Speter copying NODE_ABSPATH from SRC_ABSPATH. 937251881Speter 938251881Speter Only use this function when you know what you're doing. This function 939251881Speter explicitly ignores some case insensitivity issues! 940251881Speter 941251881Speter */ 942251881Speterstatic svn_error_t * 943251881Speterremove_node_conflict_markers(svn_wc__db_t *db, 944251881Speter const char *src_abspath, 945251881Speter const char *node_abspath, 946251881Speter apr_pool_t *scratch_pool) 947251881Speter{ 948251881Speter svn_skel_t *conflict; 949251881Speter 950289180Speter SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, 951289180Speter db, src_abspath, 952251881Speter scratch_pool, scratch_pool)); 953251881Speter 954251881Speter /* Do we have conflict markers that should be removed? */ 955251881Speter if (conflict != NULL) 956251881Speter { 957251881Speter const apr_array_header_t *markers; 958251881Speter int i; 959251881Speter const char *src_dir = svn_dirent_dirname(src_abspath, scratch_pool); 960251881Speter const char *dst_dir = svn_dirent_dirname(node_abspath, scratch_pool); 961251881Speter 962251881Speter SVN_ERR(svn_wc__conflict_read_markers(&markers, db, src_abspath, 963251881Speter conflict, 964251881Speter scratch_pool, scratch_pool)); 965251881Speter 966251881Speter /* No iterpool: Maximum number of possible conflict markers is 4 */ 967251881Speter for (i = 0; markers && (i < markers->nelts); i++) 968251881Speter { 969251881Speter const char *marker_abspath; 970251881Speter const char *child_relpath; 971286506Speter const char *child_abspath; 972251881Speter 973251881Speter marker_abspath = APR_ARRAY_IDX(markers, i, const char *); 974251881Speter 975286506Speter child_relpath = svn_dirent_skip_ancestor(src_dir, marker_abspath); 976251881Speter 977251881Speter if (child_relpath) 978251881Speter { 979286506Speter child_abspath = svn_dirent_join(dst_dir, child_relpath, 980286506Speter scratch_pool); 981251881Speter 982286506Speter SVN_ERR(svn_io_remove_file2(child_abspath, TRUE, scratch_pool)); 983251881Speter } 984251881Speter } 985251881Speter } 986251881Speter 987251881Speter return SVN_NO_ERROR; 988251881Speter} 989251881Speter 990251881Speter/* Remove all the conflict markers below SRC_DIR_ABSPATH, that were left over 991251881Speter after copying WC_DIR_ABSPATH from SRC_DIR_ABSPATH. 992251881Speter 993251881Speter This function doesn't remove the conflict markers on WC_DIR_ABSPATH 994251881Speter itself! 995251881Speter 996251881Speter Only use this function when you know what you're doing. This function 997251881Speter explicitly ignores some case insensitivity issues! 998251881Speter */ 999251881Speterstatic svn_error_t * 1000251881Speterremove_all_conflict_markers(svn_wc__db_t *db, 1001251881Speter const char *src_dir_abspath, 1002286506Speter const char *dst_dir_abspath, 1003289180Speter svn_cancel_func_t cancel_func, 1004289180Speter void *cancel_baton, 1005251881Speter apr_pool_t *scratch_pool) 1006251881Speter{ 1007251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1008251881Speter apr_hash_t *nodes; 1009251881Speter apr_hash_t *conflicts; /* Unused */ 1010251881Speter apr_hash_index_t *hi; 1011251881Speter 1012251881Speter /* Reuse a status helper to obtain all subdirs and conflicts in a single 1013251881Speter db transaction. */ 1014251881Speter /* ### This uses a rifle to kill a fly. But at least it doesn't use heavy 1015251881Speter artillery. */ 1016251881Speter SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, 1017251881Speter src_dir_abspath, 1018289180Speter FALSE /* base_tree_only */, 1019251881Speter scratch_pool, iterpool)); 1020251881Speter 1021251881Speter for (hi = apr_hash_first(scratch_pool, nodes); 1022251881Speter hi; 1023251881Speter hi = apr_hash_next(hi)) 1024251881Speter { 1025289180Speter const char *name = apr_hash_this_key(hi); 1026289180Speter struct svn_wc__db_info_t *info = apr_hash_this_val(hi); 1027251881Speter 1028289180Speter if (cancel_func) 1029289180Speter SVN_ERR(cancel_func(cancel_baton)); 1030289180Speter 1031251881Speter if (info->conflicted) 1032251881Speter { 1033251881Speter svn_pool_clear(iterpool); 1034251881Speter SVN_ERR(remove_node_conflict_markers( 1035251881Speter db, 1036251881Speter svn_dirent_join(src_dir_abspath, name, iterpool), 1037286506Speter svn_dirent_join(dst_dir_abspath, name, iterpool), 1038251881Speter iterpool)); 1039251881Speter } 1040251881Speter if (info->kind == svn_node_dir) 1041251881Speter { 1042251881Speter svn_pool_clear(iterpool); 1043251881Speter SVN_ERR(remove_all_conflict_markers( 1044251881Speter db, 1045251881Speter svn_dirent_join(src_dir_abspath, name, iterpool), 1046286506Speter svn_dirent_join(dst_dir_abspath, name, iterpool), 1047289180Speter cancel_func, cancel_baton, 1048251881Speter iterpool)); 1049251881Speter } 1050251881Speter } 1051251881Speter 1052251881Speter svn_pool_destroy(iterpool); 1053251881Speter return SVN_NO_ERROR; 1054251881Speter} 1055251881Speter 1056251881Spetersvn_error_t * 1057251881Spetersvn_wc__move2(svn_wc_context_t *wc_ctx, 1058251881Speter const char *src_abspath, 1059251881Speter const char *dst_abspath, 1060251881Speter svn_boolean_t metadata_only, 1061251881Speter svn_boolean_t allow_mixed_revisions, 1062251881Speter svn_cancel_func_t cancel_func, 1063251881Speter void *cancel_baton, 1064251881Speter svn_wc_notify_func2_t notify_func, 1065251881Speter void *notify_baton, 1066251881Speter apr_pool_t *scratch_pool) 1067251881Speter{ 1068251881Speter svn_wc__db_t *db = wc_ctx->db; 1069289180Speter svn_boolean_t record_on_delete = TRUE; 1070251881Speter svn_node_kind_t kind; 1071251881Speter svn_boolean_t conflicted; 1072251881Speter 1073251881Speter /* Verify that we have the required write locks. */ 1074251881Speter SVN_ERR(svn_wc__write_check(wc_ctx->db, 1075251881Speter svn_dirent_dirname(src_abspath, scratch_pool), 1076251881Speter scratch_pool)); 1077251881Speter SVN_ERR(svn_wc__write_check(wc_ctx->db, 1078251881Speter svn_dirent_dirname(dst_abspath, scratch_pool), 1079251881Speter scratch_pool)); 1080251881Speter 1081289180Speter SVN_ERR(copy_or_move(&record_on_delete, 1082251881Speter wc_ctx, src_abspath, dst_abspath, 1083251881Speter TRUE /* metadata_only */, 1084251881Speter TRUE /* is_move */, 1085251881Speter allow_mixed_revisions, 1086251881Speter cancel_func, cancel_baton, 1087251881Speter notify_func, notify_baton, 1088251881Speter scratch_pool)); 1089251881Speter 1090251881Speter /* An interrupt at this point will leave the new copy marked as 1091251881Speter moved-here but the source has not yet been deleted or marked as 1092251881Speter moved-to. */ 1093251881Speter 1094251881Speter /* Should we be using a workqueue for this move? It's not clear. 1095251881Speter What should happen if the copy above is interrupted? The user 1096251881Speter may want to abort the move and a workqueue might interfere with 1097251881Speter that. 1098251881Speter 1099251881Speter BH: On Windows it is not unlikely to encounter an access denied on 1100251881Speter this line. Installing the move in the workqueue via the copy_or_move 1101251881Speter might make it hard to recover from that situation, while the DB 1102251881Speter is still in a valid state. So be careful when switching this over 1103251881Speter to the workqueue. */ 1104251881Speter if (!metadata_only) 1105289180Speter { 1106289180Speter svn_error_t *err; 1107251881Speter 1108362181Sdim err = svn_error_trace(svn_io_file_rename2(src_abspath, dst_abspath, 1109362181Sdim FALSE, scratch_pool)); 1110289180Speter 1111289180Speter /* Let's try if we can keep wc.db consistent even when the move 1112289180Speter fails. Deleting the target is a wc.db only operation, while 1113289180Speter going forward (delaying the error) would try to change 1114289180Speter conflict markers, which might also fail. */ 1115289180Speter if (err) 1116289180Speter return svn_error_trace( 1117289180Speter svn_error_compose_create( 1118289180Speter err, 1119289180Speter svn_wc__db_op_delete(wc_ctx->db, dst_abspath, NULL, TRUE, 1120289180Speter NULL, NULL, cancel_func, cancel_baton, 1121289180Speter NULL, NULL, 1122289180Speter scratch_pool))); 1123289180Speter } 1124289180Speter 1125251881Speter SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL, 1126251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1127251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 1128251881Speter &conflicted, NULL, NULL, NULL, 1129251881Speter NULL, NULL, NULL, 1130251881Speter db, src_abspath, 1131251881Speter scratch_pool, scratch_pool)); 1132251881Speter 1133251881Speter if (kind == svn_node_dir) 1134251881Speter SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath, 1135289180Speter cancel_func, cancel_baton, 1136251881Speter scratch_pool)); 1137251881Speter 1138251881Speter if (conflicted) 1139286506Speter { 1140286506Speter /* When we moved a directory, we moved the conflict markers 1141286506Speter with the target... if we moved a file we only moved the 1142286506Speter file itself and the markers are still in the old location */ 1143286506Speter SVN_ERR(remove_node_conflict_markers(db, src_abspath, 1144286506Speter (kind == svn_node_dir) 1145286506Speter ? dst_abspath 1146286506Speter : src_abspath, 1147286506Speter scratch_pool)); 1148286506Speter } 1149251881Speter 1150251881Speter SVN_ERR(svn_wc__db_op_delete(db, src_abspath, 1151289180Speter record_on_delete ? dst_abspath : NULL, 1152251881Speter TRUE /* delete_dir_externals */, 1153251881Speter NULL /* conflict */, NULL /* work_items */, 1154251881Speter cancel_func, cancel_baton, 1155251881Speter notify_func, notify_baton, 1156251881Speter scratch_pool)); 1157251881Speter 1158251881Speter return SVN_NO_ERROR; 1159251881Speter} 1160