2 * copy.c: wc 'copy' functionality.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 /* ==================================================================== */
31 #include "svn_pools.h"
32 #include "svn_error.h"
33 #include "svn_dirent_uri.h"
38 #include "workqueue.h"
40 #include "conflicts.h"
42 #include "svn_private_config.h"
43 #include "private/svn_wc_private.h"
45 /* #define RECORD_MIXED_MOVE */
49 /* Make a copy of the filesystem node (or tree if RECURSIVE) at
50 SRC_ABSPATH under a temporary name in the directory
51 TMPDIR_ABSPATH and return the absolute path of the copy in
52 *DST_ABSPATH. Return the node kind of SRC_ABSPATH in *KIND. If
53 SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate
54 that no copy was made.
56 If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH.
57 RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of
58 SRC_ABSPATH, and RECORDED_TIME the recorded size or 0.
60 These values will be used to avoid unneeded work.
63 copy_to_tmpdir(svn_skel_t **work_item,
64 svn_node_kind_t *kind,
66 const char *src_abspath,
67 const char *dst_abspath,
68 const char *tmpdir_abspath,
69 svn_boolean_t file_copy,
70 svn_boolean_t unversioned,
71 const svn_io_dirent2_t *dirent,
72 svn_filesize_t recorded_size,
73 apr_time_t recorded_time,
74 svn_cancel_func_t cancel_func,
76 apr_pool_t *result_pool,
77 apr_pool_t *scratch_pool)
79 svn_boolean_t is_special;
80 svn_io_file_del_t delete_when;
81 const char *dst_tmp_abspath;
82 svn_node_kind_t dsk_kind;
91 is_special = dirent->special;
94 SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special,
96 if (*kind == svn_node_none)
100 else if (*kind == svn_node_unknown)
102 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
103 _("Source '%s' is unexpected kind"),
104 svn_dirent_local_style(src_abspath,
107 else if (*kind == svn_node_dir || is_special)
108 delete_when = svn_io_file_del_on_close;
109 else /* the default case: (*kind == svn_node_file) */
110 delete_when = svn_io_file_del_none;
112 /* ### Do we need a pool cleanup to remove the copy? We can't use
113 ### svn_io_file_del_on_pool_cleanup above because a) it won't
114 ### handle the directory case and b) we need to be able to remove
115 ### the cleanup before queueing the move work item. */
117 if (file_copy && !unversioned)
119 svn_boolean_t modified;
120 /* It's faster to look for mods on the source now, as
121 the timestamp might match, than to examine the
122 destination later as the destination timestamp will
126 && dirent->kind == svn_node_file
127 && recorded_size != SVN_INVALID_FILESIZE
128 && recorded_size == dirent->filesize
129 && recorded_time == dirent->mtime)
131 modified = FALSE; /* Recorded matches on-disk. Easy out */
135 SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, src_abspath,
136 FALSE, scratch_pool));
141 /* Why create a temp copy if we can just reinstall from pristine? */
142 SVN_ERR(svn_wc__wq_build_file_install(work_item,
143 db, dst_abspath, NULL, FALSE,
145 result_pool, scratch_pool));
149 else if (*kind == svn_node_dir && !file_copy)
151 /* Just build a new direcory from the workqueue */
152 SVN_ERR(svn_wc__wq_build_dir_install(work_item,
154 result_pool, scratch_pool));
159 /* Set DST_TMP_ABSPATH to a temporary unique path. If *KIND is file, leave
160 a file there and then overwrite it; otherwise leave no node on disk at
161 that path. In the latter case, something else might use that path
162 before we get around to using it a moment later, but never mind. */
163 SVN_ERR(svn_io_open_unique_file3(NULL, &dst_tmp_abspath, tmpdir_abspath,
164 delete_when, scratch_pool, scratch_pool));
166 if (*kind == svn_node_dir)
169 SVN_ERR(svn_io_copy_dir_recursively(
172 svn_dirent_basename(dst_tmp_abspath, scratch_pool),
173 TRUE, /* copy_perms */
174 cancel_func, cancel_baton,
177 SVN_ERR(svn_io_dir_make(dst_tmp_abspath, APR_OS_DEFAULT, scratch_pool));
179 else if (!is_special)
180 SVN_ERR(svn_io_copy_file(src_abspath, dst_tmp_abspath,
181 TRUE /* copy_perms */,
184 SVN_ERR(svn_io_copy_link(src_abspath, dst_tmp_abspath, scratch_pool));
188 /* Remove 'read-only' from the destination file; it's a local add now. */
189 SVN_ERR(svn_io_set_file_read_write(dst_tmp_abspath,
190 FALSE, scratch_pool));
193 SVN_ERR(svn_wc__wq_build_file_move(work_item, db, dst_abspath,
194 dst_tmp_abspath, dst_abspath,
195 result_pool, scratch_pool));
200 /* Copy the versioned file SRC_ABSPATH in DB to the path DST_ABSPATH in DB.
201 If METADATA_ONLY is true, copy only the versioned metadata,
202 otherwise copy both the versioned metadata and the filesystem node (even
203 if it is the wrong kind, and recursively if it is a dir).
205 If IS_MOVE is true, record move information in working copy meta
206 data in addition to copying the file.
208 If the versioned file has a text conflict, and the .mine file exists in
209 the filesystem, copy the .mine file to DST_ABSPATH. Otherwise, copy the
210 versioned file itself.
212 This also works for versioned symlinks that are stored in the db as
213 svn_node_file with svn:special set.
215 If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH.
216 RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of
217 SRC_ABSPATH, and RECORDED_TIME the recorded size or 0.
219 These values will be used to avoid unneeded work.
222 copy_versioned_file(svn_wc__db_t *db,
223 const char *src_abspath,
224 const char *dst_abspath,
225 const char *dst_op_root_abspath,
226 const char *tmpdir_abspath,
227 svn_boolean_t metadata_only,
228 svn_boolean_t conflicted,
229 svn_boolean_t is_move,
230 const svn_io_dirent2_t *dirent,
231 svn_filesize_t recorded_size,
232 apr_time_t recorded_time,
233 svn_cancel_func_t cancel_func,
235 svn_wc_notify_func2_t notify_func,
237 apr_pool_t *scratch_pool)
239 svn_skel_t *work_items = NULL;
241 /* In case we are copying from one WC to another (e.g. an external dir),
242 ensure the destination WC has a copy of the pristine text. */
244 /* Prepare a temp copy of the filesystem node. It is usually a file, but
245 copy recursively if it's a dir. */
248 const char *my_src_abspath = NULL;
249 svn_boolean_t handle_as_unversioned = FALSE;
251 /* By default, take the copy source as given. */
252 my_src_abspath = src_abspath;
256 svn_skel_t *conflict;
257 const char *conflict_working;
260 /* Is there a text conflict at the source path? */
261 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
263 scratch_pool, scratch_pool));
265 err = svn_wc__conflict_read_text_conflict(&conflict_working, NULL, NULL,
266 db, src_abspath, conflict,
270 if (err && err->apr_err == SVN_ERR_WC_MISSING)
272 /* not text conflicted */
273 svn_error_clear(err);
274 conflict_working = NULL;
279 if (conflict_working)
281 svn_node_kind_t working_kind;
283 /* Does the ".mine" file exist? */
284 SVN_ERR(svn_io_check_path(conflict_working, &working_kind,
287 if (working_kind == svn_node_file)
289 /* Don't perform unmodified/pristine optimization */
290 handle_as_unversioned = TRUE;
291 my_src_abspath = conflict_working;
296 SVN_ERR(copy_to_tmpdir(&work_items, NULL, db, my_src_abspath,
297 dst_abspath, tmpdir_abspath,
298 TRUE /* file_copy */,
299 handle_as_unversioned /* unversioned */,
300 dirent, recorded_size, recorded_time,
301 cancel_func, cancel_baton,
302 scratch_pool, scratch_pool));
305 /* Copy the (single) node's metadata, and move the new filesystem node
307 SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath,
308 dst_op_root_abspath, is_move, work_items,
313 svn_wc_notify_t *notify
314 = svn_wc_create_notify(dst_abspath, svn_wc_notify_add,
316 notify->kind = svn_node_file;
318 (*notify_func)(notify_baton, notify, scratch_pool);
323 /* Copy the versioned dir SRC_ABSPATH in DB to the path DST_ABSPATH in DB,
324 recursively. If METADATA_ONLY is true, copy only the versioned metadata,
325 otherwise copy both the versioned metadata and the filesystem nodes (even
326 if they are the wrong kind, and including unversioned children).
327 If IS_MOVE is true, record move information in working copy meta
328 data in addition to copying the directory.
330 WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root)
332 If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH.
335 copy_versioned_dir(svn_wc__db_t *db,
336 const char *src_abspath,
337 const char *dst_abspath,
338 const char *dst_op_root_abspath,
339 const char *tmpdir_abspath,
340 svn_boolean_t metadata_only,
341 svn_boolean_t is_move,
342 const svn_io_dirent2_t *dirent,
343 svn_cancel_func_t cancel_func,
345 svn_wc_notify_func2_t notify_func,
347 apr_pool_t *scratch_pool)
349 svn_skel_t *work_items = NULL;
350 const char *dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
351 apr_hash_t *versioned_children;
352 apr_hash_t *conflicted_children;
353 apr_hash_t *disk_children;
354 apr_hash_index_t *hi;
355 svn_node_kind_t disk_kind;
356 apr_pool_t *iterpool;
358 /* Prepare a temp copy of the single filesystem node (usually a dir). */
361 SVN_ERR(copy_to_tmpdir(&work_items, &disk_kind,
362 db, src_abspath, dst_abspath,
364 FALSE /* file_copy */,
365 FALSE /* unversioned */,
366 dirent, SVN_INVALID_FILESIZE, 0,
367 cancel_func, cancel_baton,
368 scratch_pool, scratch_pool));
371 /* Copy the (single) node's metadata, and move the new filesystem node
373 SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath,
374 dst_op_root_abspath, is_move, work_items,
379 svn_wc_notify_t *notify
380 = svn_wc_create_notify(dst_abspath, svn_wc_notify_add,
382 notify->kind = svn_node_dir;
384 /* When we notify that we performed a copy, make sure we already did */
385 if (work_items != NULL)
386 SVN_ERR(svn_wc__wq_run(db, dir_abspath,
387 cancel_func, cancel_baton, scratch_pool));
389 (*notify_func)(notify_baton, notify, scratch_pool);
392 if (!metadata_only && disk_kind == svn_node_dir)
393 /* All filesystem children, versioned and unversioned. We're only
394 interested in their names, so we can pass TRUE as the only_check_type
396 SVN_ERR(svn_io_get_dirents3(&disk_children, src_abspath, TRUE,
397 scratch_pool, scratch_pool));
399 disk_children = NULL;
401 /* Copy all the versioned children */
402 iterpool = svn_pool_create(scratch_pool);
403 SVN_ERR(svn_wc__db_read_children_info(&versioned_children,
404 &conflicted_children,
406 FALSE /* base_tree_only */,
407 scratch_pool, iterpool));
408 for (hi = apr_hash_first(scratch_pool, versioned_children);
410 hi = apr_hash_next(hi))
412 const char *child_name, *child_src_abspath, *child_dst_abspath;
413 struct svn_wc__db_info_t *info;
415 svn_pool_clear(iterpool);
418 SVN_ERR(cancel_func(cancel_baton));
420 child_name = apr_hash_this_key(hi);
421 info = apr_hash_this_val(hi);
422 child_src_abspath = svn_dirent_join(src_abspath, child_name, iterpool);
423 child_dst_abspath = svn_dirent_join(dst_abspath, child_name, iterpool);
426 SVN_ERR(svn_wc__db_op_copy_shadowed_layer(db,
432 if (info->status == svn_wc__db_status_normal
433 || info->status == svn_wc__db_status_added)
435 /* We have more work to do than just changing the DB */
436 if (info->kind == svn_node_file)
438 /* We should skip this node if this child is a file external
439 (issues #3589, #4000) */
440 if (!info->file_external)
441 SVN_ERR(copy_versioned_file(db,
446 metadata_only, info->conflicted,
449 ? svn_hash_gets(disk_children,
454 cancel_func, cancel_baton,
458 else if (info->kind == svn_node_dir)
459 SVN_ERR(copy_versioned_dir(db,
460 child_src_abspath, child_dst_abspath,
461 dst_op_root_abspath, tmpdir_abspath,
462 metadata_only, is_move,
464 ? svn_hash_gets(disk_children,
467 cancel_func, cancel_baton, NULL, NULL,
470 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
471 _("cannot handle node kind for '%s'"),
472 svn_dirent_local_style(child_src_abspath,
475 else if (info->status == svn_wc__db_status_deleted
476 || info->status == svn_wc__db_status_not_present
477 || info->status == svn_wc__db_status_excluded)
479 /* This will be copied as some kind of deletion. Don't touch
481 SVN_ERR(svn_wc__db_op_copy(db, child_src_abspath,
482 child_dst_abspath, dst_op_root_abspath,
483 is_move, NULL, iterpool));
485 /* Don't recurse on children when all we do is creating not-present
488 else if (info->status == svn_wc__db_status_incomplete)
490 /* Should go ahead and copy incomplete to incomplete? Try to
491 copy as much as possible, or give up early? */
492 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
493 _("Cannot handle status of '%s'"),
494 svn_dirent_local_style(child_src_abspath,
499 SVN_ERR_ASSERT(info->status == svn_wc__db_status_server_excluded);
501 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
502 _("Cannot copy '%s' excluded by server"),
503 svn_dirent_local_style(child_src_abspath,
508 && (info->status == svn_wc__db_status_normal
509 || info->status == svn_wc__db_status_added))
511 /* Remove versioned child as it has been handled */
512 svn_hash_sets(disk_children, child_name, NULL);
516 /* Copy the remaining filesystem children, which are unversioned, skipping
517 any conflict-marker files. */
518 if (disk_children && apr_hash_count(disk_children))
520 apr_hash_t *marker_files;
522 SVN_ERR(svn_wc__db_get_conflict_marker_files(&marker_files, db,
523 src_abspath, scratch_pool,
528 for (hi = apr_hash_first(scratch_pool, disk_children); hi;
529 hi = apr_hash_next(hi))
531 const char *name = apr_hash_this_key(hi);
532 const char *unver_src_abspath, *unver_dst_abspath;
533 svn_skel_t *work_item;
535 if (svn_wc_is_adm_dir(name, iterpool))
539 SVN_ERR(cancel_func(cancel_baton));
541 svn_pool_clear(iterpool);
542 unver_src_abspath = svn_dirent_join(src_abspath, name, iterpool);
543 unver_dst_abspath = svn_dirent_join(dst_abspath, name, iterpool);
546 svn_hash_gets(marker_files, unver_src_abspath))
549 SVN_ERR(copy_to_tmpdir(&work_item, NULL, db, unver_src_abspath,
550 unver_dst_abspath, tmpdir_abspath,
551 TRUE /* recursive */, TRUE /* unversioned */,
552 NULL, SVN_INVALID_FILESIZE, 0,
553 cancel_func, cancel_baton,
554 scratch_pool, iterpool));
557 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
559 SVN_ERR(svn_wc__db_wq_add(db, dst_abspath, work_items, iterpool));
562 svn_pool_destroy(iterpool);
568 /* The guts of svn_wc_copy3() and svn_wc_move().
569 * The additional parameter IS_MOVE indicates whether this is a copy or
572 * If RECORD_MOVE_ON_DELETE is not NULL and a move had to be degraded
573 * to a copy, then set *RECORD_MOVE_ON_DELETE to FALSE. */
575 copy_or_move(svn_boolean_t *record_move_on_delete,
576 svn_wc_context_t *wc_ctx,
577 const char *src_abspath,
578 const char *dst_abspath,
579 svn_boolean_t metadata_only,
580 svn_boolean_t is_move,
581 svn_boolean_t allow_mixed_revisions,
582 svn_cancel_func_t cancel_func,
584 svn_wc_notify_func2_t notify_func,
586 apr_pool_t *scratch_pool)
588 svn_wc__db_t *db = wc_ctx->db;
589 svn_node_kind_t src_db_kind;
590 const char *dstdir_abspath;
591 svn_boolean_t conflicted;
592 const char *tmpdir_abspath;
593 const char *src_wcroot_abspath;
594 const char *dst_wcroot_abspath;
595 svn_boolean_t within_one_wc;
596 svn_wc__db_status_t src_status;
598 svn_filesize_t recorded_size;
599 apr_time_t recorded_time;
601 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
602 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
604 dstdir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
606 /* Ensure DSTDIR_ABSPATH belongs to the same repository as SRC_ABSPATH;
607 throw an error if not. */
609 svn_wc__db_status_t dstdir_status;
610 const char *src_repos_root_url, *dst_repos_root_url;
611 const char *src_repos_uuid, *dst_repos_uuid;
612 const char *src_repos_relpath;
614 err = svn_wc__db_read_info(&src_status, &src_db_kind, NULL,
615 &src_repos_relpath, &src_repos_root_url,
616 &src_repos_uuid, NULL, NULL, NULL, NULL, NULL,
617 NULL, NULL, NULL, NULL, NULL, NULL,
618 &recorded_size, &recorded_time,
619 NULL, &conflicted, NULL, NULL, NULL, NULL,
621 db, src_abspath, scratch_pool, scratch_pool);
623 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
625 /* Replicate old error code and text */
626 svn_error_clear(err);
627 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
628 _("'%s' is not under version control"),
629 svn_dirent_local_style(src_abspath,
635 /* Do this now, as we know the right data is cached */
636 SVN_ERR(svn_wc__db_get_wcroot(&src_wcroot_abspath, db, src_abspath,
637 scratch_pool, scratch_pool));
641 case svn_wc__db_status_deleted:
642 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
643 _("Deleted node '%s' can't be copied."),
644 svn_dirent_local_style(src_abspath,
647 case svn_wc__db_status_excluded:
648 case svn_wc__db_status_server_excluded:
649 case svn_wc__db_status_not_present:
650 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
651 _("The node '%s' was not found."),
652 svn_dirent_local_style(src_abspath,
658 if (is_move && ! strcmp(src_abspath, src_wcroot_abspath))
660 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
661 _("'%s' is the root of a working copy and "
663 svn_dirent_local_style(src_abspath,
666 if (is_move && src_repos_relpath && !src_repos_relpath[0])
668 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
669 _("'%s' represents the repository root "
670 "and cannot be moved"),
671 svn_dirent_local_style(src_abspath,
675 err = svn_wc__db_read_info(&dstdir_status, NULL, NULL, NULL,
676 &dst_repos_root_url, &dst_repos_uuid, NULL,
677 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
678 NULL, NULL, NULL, NULL, NULL, NULL,
679 NULL, NULL, NULL, NULL,
682 scratch_pool, scratch_pool);
684 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
686 /* An unversioned destination directory exists on disk. */
687 svn_error_clear(err);
688 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
689 _("'%s' is not under version control"),
690 svn_dirent_local_style(dstdir_abspath,
696 /* Do this now, as we know the right data is cached */
697 SVN_ERR(svn_wc__db_get_wcroot(&dst_wcroot_abspath, db, dstdir_abspath,
698 scratch_pool, scratch_pool));
700 if (!src_repos_root_url)
702 if (src_status == svn_wc__db_status_added)
703 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
705 &src_repos_uuid, NULL, NULL, NULL,
708 scratch_pool, scratch_pool));
710 /* If not added, the node must have a base or we can't copy */
711 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL,
713 &src_repos_uuid, NULL, NULL, NULL,
714 NULL, NULL, NULL, NULL, NULL, NULL,
717 scratch_pool, scratch_pool));
720 if (!dst_repos_root_url)
722 if (dstdir_status == svn_wc__db_status_added)
723 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
725 &dst_repos_uuid, NULL, NULL, NULL,
728 scratch_pool, scratch_pool));
730 /* If not added, the node must have a base or we can't copy */
731 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL,
733 &dst_repos_uuid, NULL, NULL, NULL,
734 NULL, NULL, NULL, NULL, NULL, NULL,
737 scratch_pool, scratch_pool));
740 if (strcmp(src_repos_root_url, dst_repos_root_url) != 0
741 || strcmp(src_repos_uuid, dst_repos_uuid) != 0)
742 return svn_error_createf(
743 SVN_ERR_WC_INVALID_SCHEDULE, NULL,
744 _("Cannot copy to '%s', as it is not from repository '%s'; "
746 svn_dirent_local_style(dst_abspath, scratch_pool),
747 src_repos_root_url, dst_repos_root_url);
749 if (dstdir_status == svn_wc__db_status_deleted)
750 return svn_error_createf(
751 SVN_ERR_WC_INVALID_SCHEDULE, NULL,
752 _("Cannot copy to '%s' as it is scheduled for deletion"),
753 svn_dirent_local_style(dst_abspath, scratch_pool));
754 /* ### should report dstdir_abspath instead of dst_abspath? */
757 /* TODO(#2843): Rework the error report. */
758 /* Check if the copy target is missing or hidden and thus not exist on the
759 disk, before actually doing the file copy. */
761 svn_wc__db_status_t dst_status;
763 err = svn_wc__db_read_info(&dst_status, NULL, NULL, NULL, NULL, NULL,
764 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
765 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
766 NULL, NULL, NULL, NULL, NULL,
767 db, dst_abspath, scratch_pool, scratch_pool);
769 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
770 return svn_error_trace(err);
772 svn_error_clear(err);
777 case svn_wc__db_status_excluded:
778 return svn_error_createf(
779 SVN_ERR_ENTRY_EXISTS, NULL,
780 _("'%s' is already under version control "
782 svn_dirent_local_style(dst_abspath, scratch_pool));
783 case svn_wc__db_status_server_excluded:
784 return svn_error_createf(
785 SVN_ERR_ENTRY_EXISTS, NULL,
786 _("'%s' is already under version control"),
787 svn_dirent_local_style(dst_abspath, scratch_pool));
789 case svn_wc__db_status_deleted:
790 case svn_wc__db_status_not_present:
791 break; /* OK to add */
795 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
796 _("There is already a versioned item '%s'"),
797 svn_dirent_local_style(dst_abspath,
802 /* Check that the target path is not obstructed, if required. */
805 svn_node_kind_t dst_kind;
807 /* (We need only to check the root of the copy, not every path inside
808 copy_versioned_file/_dir.) */
809 SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind, scratch_pool));
810 if (dst_kind != svn_node_none)
811 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
812 _("'%s' already exists and is in the way"),
813 svn_dirent_local_style(dst_abspath,
817 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
819 scratch_pool, scratch_pool));
821 within_one_wc = (strcmp(src_wcroot_abspath, dst_wcroot_abspath) == 0);
826 if (record_move_on_delete)
827 *record_move_on_delete = FALSE;
833 SVN_ERR(svn_wc__db_pristine_transfer(db, src_abspath, dst_wcroot_abspath,
834 cancel_func, cancel_baton,
837 if (src_db_kind == svn_node_file
838 || src_db_kind == svn_node_symlink)
840 err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath,
842 metadata_only, conflicted, is_move,
843 NULL, recorded_size, recorded_time,
844 cancel_func, cancel_baton,
845 notify_func, notify_baton,
851 && src_status == svn_wc__db_status_normal)
853 svn_revnum_t min_rev;
854 svn_revnum_t max_rev;
856 /* Verify that the move source is a single-revision subtree. */
857 SVN_ERR(svn_wc__db_min_max_revisions(&min_rev, &max_rev, db,
858 src_abspath, FALSE, scratch_pool));
859 if (SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev) &&
862 if (!allow_mixed_revisions)
863 return svn_error_createf(SVN_ERR_WC_MIXED_REVISIONS, NULL,
864 _("Cannot move mixed-revision "
865 "subtree '%s' [%ld:%ld]; "
866 "try updating it first"),
867 svn_dirent_local_style(src_abspath,
871 #ifndef RECORD_MIXED_MOVE
873 if (record_move_on_delete)
874 *record_move_on_delete = FALSE;
879 err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath,
880 tmpdir_abspath, metadata_only, is_move,
882 cancel_func, cancel_baton,
883 notify_func, notify_baton,
887 if (err && svn_error_find_cause(err, SVN_ERR_CANCELLED))
888 return svn_error_trace(err);
891 err = svn_error_compose_create(err,
892 svn_wc__db_op_handle_move_back(NULL,
893 db, dst_abspath, src_abspath,
894 NULL /* work_items */,
897 /* Run the work queue with the remaining work */
898 SVN_ERR(svn_error_compose_create(
900 svn_wc__wq_run(db, dst_abspath,
901 cancel_func, cancel_baton,
908 /* Public Interface */
911 svn_wc_copy3(svn_wc_context_t *wc_ctx,
912 const char *src_abspath,
913 const char *dst_abspath,
914 svn_boolean_t metadata_only,
915 svn_cancel_func_t cancel_func,
917 svn_wc_notify_func2_t notify_func,
919 apr_pool_t *scratch_pool)
921 /* Verify that we have the required write lock. */
922 SVN_ERR(svn_wc__write_check(wc_ctx->db,
923 svn_dirent_dirname(dst_abspath, scratch_pool),
926 return svn_error_trace(copy_or_move(NULL, wc_ctx, src_abspath, dst_abspath,
927 metadata_only, FALSE /* is_move */,
928 TRUE /* allow_mixed_revisions */,
929 cancel_func, cancel_baton,
930 notify_func, notify_baton,
935 /* Remove the conflict markers of NODE_ABSPATH, that were left over after
936 copying NODE_ABSPATH from SRC_ABSPATH.
938 Only use this function when you know what you're doing. This function
939 explicitly ignores some case insensitivity issues!
943 remove_node_conflict_markers(svn_wc__db_t *db,
944 const char *src_abspath,
945 const char *node_abspath,
946 apr_pool_t *scratch_pool)
948 svn_skel_t *conflict;
950 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
952 scratch_pool, scratch_pool));
954 /* Do we have conflict markers that should be removed? */
955 if (conflict != NULL)
957 const apr_array_header_t *markers;
959 const char *src_dir = svn_dirent_dirname(src_abspath, scratch_pool);
960 const char *dst_dir = svn_dirent_dirname(node_abspath, scratch_pool);
962 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, src_abspath,
964 scratch_pool, scratch_pool));
966 /* No iterpool: Maximum number of possible conflict markers is 4 */
967 for (i = 0; markers && (i < markers->nelts); i++)
969 const char *marker_abspath;
970 const char *child_relpath;
971 const char *child_abspath;
973 marker_abspath = APR_ARRAY_IDX(markers, i, const char *);
975 child_relpath = svn_dirent_skip_ancestor(src_dir, marker_abspath);
979 child_abspath = svn_dirent_join(dst_dir, child_relpath,
982 SVN_ERR(svn_io_remove_file2(child_abspath, TRUE, scratch_pool));
990 /* Remove all the conflict markers below SRC_DIR_ABSPATH, that were left over
991 after copying WC_DIR_ABSPATH from SRC_DIR_ABSPATH.
993 This function doesn't remove the conflict markers on WC_DIR_ABSPATH
996 Only use this function when you know what you're doing. This function
997 explicitly ignores some case insensitivity issues!
1000 remove_all_conflict_markers(svn_wc__db_t *db,
1001 const char *src_dir_abspath,
1002 const char *dst_dir_abspath,
1003 svn_cancel_func_t cancel_func,
1005 apr_pool_t *scratch_pool)
1007 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1009 apr_hash_t *conflicts; /* Unused */
1010 apr_hash_index_t *hi;
1012 /* Reuse a status helper to obtain all subdirs and conflicts in a single
1014 /* ### This uses a rifle to kill a fly. But at least it doesn't use heavy
1016 SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db,
1018 FALSE /* base_tree_only */,
1019 scratch_pool, iterpool));
1021 for (hi = apr_hash_first(scratch_pool, nodes);
1023 hi = apr_hash_next(hi))
1025 const char *name = apr_hash_this_key(hi);
1026 struct svn_wc__db_info_t *info = apr_hash_this_val(hi);
1029 SVN_ERR(cancel_func(cancel_baton));
1031 if (info->conflicted)
1033 svn_pool_clear(iterpool);
1034 SVN_ERR(remove_node_conflict_markers(
1036 svn_dirent_join(src_dir_abspath, name, iterpool),
1037 svn_dirent_join(dst_dir_abspath, name, iterpool),
1040 if (info->kind == svn_node_dir)
1042 svn_pool_clear(iterpool);
1043 SVN_ERR(remove_all_conflict_markers(
1045 svn_dirent_join(src_dir_abspath, name, iterpool),
1046 svn_dirent_join(dst_dir_abspath, name, iterpool),
1047 cancel_func, cancel_baton,
1052 svn_pool_destroy(iterpool);
1053 return SVN_NO_ERROR;
1057 svn_wc__move2(svn_wc_context_t *wc_ctx,
1058 const char *src_abspath,
1059 const char *dst_abspath,
1060 svn_boolean_t metadata_only,
1061 svn_boolean_t allow_mixed_revisions,
1062 svn_cancel_func_t cancel_func,
1064 svn_wc_notify_func2_t notify_func,
1066 apr_pool_t *scratch_pool)
1068 svn_wc__db_t *db = wc_ctx->db;
1069 svn_boolean_t record_on_delete = TRUE;
1070 svn_node_kind_t kind;
1071 svn_boolean_t conflicted;
1073 /* Verify that we have the required write locks. */
1074 SVN_ERR(svn_wc__write_check(wc_ctx->db,
1075 svn_dirent_dirname(src_abspath, scratch_pool),
1077 SVN_ERR(svn_wc__write_check(wc_ctx->db,
1078 svn_dirent_dirname(dst_abspath, scratch_pool),
1081 SVN_ERR(copy_or_move(&record_on_delete,
1082 wc_ctx, src_abspath, dst_abspath,
1083 TRUE /* metadata_only */,
1085 allow_mixed_revisions,
1086 cancel_func, cancel_baton,
1087 notify_func, notify_baton,
1090 /* An interrupt at this point will leave the new copy marked as
1091 moved-here but the source has not yet been deleted or marked as
1094 /* Should we be using a workqueue for this move? It's not clear.
1095 What should happen if the copy above is interrupted? The user
1096 may want to abort the move and a workqueue might interfere with
1099 BH: On Windows it is not unlikely to encounter an access denied on
1100 this line. Installing the move in the workqueue via the copy_or_move
1101 might make it hard to recover from that situation, while the DB
1102 is still in a valid state. So be careful when switching this over
1103 to the workqueue. */
1108 err = svn_error_trace(svn_io_file_rename2(src_abspath, dst_abspath,
1109 FALSE, scratch_pool));
1111 /* Let's try if we can keep wc.db consistent even when the move
1112 fails. Deleting the target is a wc.db only operation, while
1113 going forward (delaying the error) would try to change
1114 conflict markers, which might also fail. */
1116 return svn_error_trace(
1117 svn_error_compose_create(
1119 svn_wc__db_op_delete(wc_ctx->db, dst_abspath, NULL, TRUE,
1120 NULL, NULL, cancel_func, cancel_baton,
1125 SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
1126 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1127 NULL, NULL, NULL, NULL, NULL, NULL,
1128 &conflicted, NULL, NULL, NULL,
1131 scratch_pool, scratch_pool));
1133 if (kind == svn_node_dir)
1134 SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath,
1135 cancel_func, cancel_baton,
1140 /* When we moved a directory, we moved the conflict markers
1141 with the target... if we moved a file we only moved the
1142 file itself and the markers are still in the old location */
1143 SVN_ERR(remove_node_conflict_markers(db, src_abspath,
1144 (kind == svn_node_dir)
1150 SVN_ERR(svn_wc__db_op_delete(db, src_abspath,
1151 record_on_delete ? dst_abspath : NULL,
1152 TRUE /* delete_dir_externals */,
1153 NULL /* conflict */, NULL /* work_items */,
1154 cancel_func, cancel_baton,
1155 notify_func, notify_baton,
1158 return SVN_NO_ERROR;