]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_wc/copy.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_wc / copy.c
1 /*
2  * copy.c:  wc 'copy' functionality.
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29
30 #include <string.h>
31 #include "svn_pools.h"
32 #include "svn_error.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_path.h"
35 #include "svn_hash.h"
36
37 #include "wc.h"
38 #include "workqueue.h"
39 #include "props.h"
40 #include "conflicts.h"
41
42 #include "svn_private_config.h"
43 #include "private/svn_wc_private.h"
44
45 \f
46 /*** Code. ***/
47
48 /* Make a copy of the filesystem node (or tree if RECURSIVE) at
49    SRC_ABSPATH under a temporary name in the directory
50    TMPDIR_ABSPATH and return the absolute path of the copy in
51    *DST_ABSPATH.  Return the node kind of SRC_ABSPATH in *KIND.  If
52    SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate
53    that no copy was made. */
54 static svn_error_t *
55 copy_to_tmpdir(svn_skel_t **work_item,
56                svn_node_kind_t *kind,
57                svn_wc__db_t *db,
58                const char *src_abspath,
59                const char *dst_abspath,
60                const char *tmpdir_abspath,
61                svn_boolean_t file_copy,
62                svn_boolean_t unversioned,
63                svn_cancel_func_t cancel_func,
64                void *cancel_baton,
65                apr_pool_t *result_pool,
66                apr_pool_t *scratch_pool)
67 {
68   svn_boolean_t is_special;
69   svn_io_file_del_t delete_when;
70   const char *dst_tmp_abspath;
71   svn_node_kind_t dsk_kind;
72   if (!kind)
73     kind = &dsk_kind;
74
75   *work_item = NULL;
76
77   SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special,
78                                     scratch_pool));
79   if (*kind == svn_node_none)
80     {
81       return SVN_NO_ERROR;
82     }
83   else if (*kind == svn_node_unknown)
84     {
85       return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
86                                _("Source '%s' is unexpected kind"),
87                                svn_dirent_local_style(src_abspath,
88                                                       scratch_pool));
89     }
90   else if (*kind == svn_node_dir || is_special)
91     delete_when = svn_io_file_del_on_close;
92   else /* the default case: (*kind == svn_node_file) */
93     delete_when = svn_io_file_del_none;
94
95   /* ### Do we need a pool cleanup to remove the copy?  We can't use
96      ### svn_io_file_del_on_pool_cleanup above because a) it won't
97      ### handle the directory case and b) we need to be able to remove
98      ### the cleanup before queueing the move work item. */
99
100   if (file_copy && !unversioned)
101     {
102       svn_boolean_t modified;
103       /* It's faster to look for mods on the source now, as
104          the timestamp might match, than to examine the
105          destination later as the destination timestamp will
106          never match. */
107       SVN_ERR(svn_wc__internal_file_modified_p(&modified,
108                                                db, src_abspath,
109                                                FALSE, scratch_pool));
110       if (!modified)
111         {
112           /* Why create a temp copy if we can just reinstall from pristine? */
113           SVN_ERR(svn_wc__wq_build_file_install(work_item,
114                                                 db, dst_abspath, NULL, FALSE,
115                                                 TRUE,
116                                                 result_pool, scratch_pool));
117           return SVN_NO_ERROR;
118         }
119     }
120
121   /* Set DST_TMP_ABSPATH to a temporary unique path.  If *KIND is file, leave
122      a file there and then overwrite it; otherwise leave no node on disk at
123      that path.  In the latter case, something else might use that path
124      before we get around to using it a moment later, but never mind. */
125   SVN_ERR(svn_io_open_unique_file3(NULL, &dst_tmp_abspath, tmpdir_abspath,
126                                    delete_when, scratch_pool, scratch_pool));
127
128   if (*kind == svn_node_dir)
129     {
130       if (file_copy)
131         SVN_ERR(svn_io_copy_dir_recursively(
132                            src_abspath,
133                            tmpdir_abspath,
134                            svn_dirent_basename(dst_tmp_abspath, scratch_pool),
135                            TRUE, /* copy_perms */
136                            cancel_func, cancel_baton,
137                            scratch_pool));
138       else
139         SVN_ERR(svn_io_dir_make(dst_tmp_abspath, APR_OS_DEFAULT, scratch_pool));
140     }
141   else if (!is_special)
142     SVN_ERR(svn_io_copy_file(src_abspath, dst_tmp_abspath,
143                              TRUE /* copy_perms */,
144                              scratch_pool));
145   else
146     SVN_ERR(svn_io_copy_link(src_abspath, dst_tmp_abspath, scratch_pool));
147
148   if (file_copy)
149     {
150       /* Remove 'read-only' from the destination file; it's a local add now. */
151       SVN_ERR(svn_io_set_file_read_write(dst_tmp_abspath,
152                                          FALSE, scratch_pool));
153     }
154
155   SVN_ERR(svn_wc__wq_build_file_move(work_item, db, dst_abspath,
156                                      dst_tmp_abspath, dst_abspath,
157                                      result_pool, scratch_pool));
158
159   return SVN_NO_ERROR;
160 }
161
162 /* Copy the versioned file SRC_ABSPATH in DB to the path DST_ABSPATH in DB.
163    If METADATA_ONLY is true, copy only the versioned metadata,
164    otherwise copy both the versioned metadata and the filesystem node (even
165    if it is the wrong kind, and recursively if it is a dir).
166
167    If IS_MOVE is true, record move information in working copy meta
168    data in addition to copying the file.
169
170    If the versioned file has a text conflict, and the .mine file exists in
171    the filesystem, copy the .mine file to DST_ABSPATH.  Otherwise, copy the
172    versioned file itself.
173
174    This also works for versioned symlinks that are stored in the db as
175    svn_node_file with svn:special set. */
176 static svn_error_t *
177 copy_versioned_file(svn_wc__db_t *db,
178                     const char *src_abspath,
179                     const char *dst_abspath,
180                     const char *dst_op_root_abspath,
181                     const char *tmpdir_abspath,
182                     svn_boolean_t metadata_only,
183                     svn_boolean_t conflicted,
184                     svn_boolean_t is_move,
185                     svn_cancel_func_t cancel_func,
186                     void *cancel_baton,
187                     svn_wc_notify_func2_t notify_func,
188                     void *notify_baton,
189                     apr_pool_t *scratch_pool)
190 {
191   svn_skel_t *work_items = NULL;
192
193   /* In case we are copying from one WC to another (e.g. an external dir),
194      ensure the destination WC has a copy of the pristine text. */
195
196   /* Prepare a temp copy of the filesystem node.  It is usually a file, but
197      copy recursively if it's a dir. */
198   if (!metadata_only)
199     {
200       const char *my_src_abspath = NULL;
201       svn_boolean_t handle_as_unversioned = FALSE;
202
203       /* By default, take the copy source as given. */
204       my_src_abspath = src_abspath;
205
206       if (conflicted)
207         {
208           svn_skel_t *conflict;
209           const char *conflict_working;
210           svn_error_t *err;
211
212           /* Is there a text conflict at the source path? */
213           SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath,
214                                          scratch_pool, scratch_pool));
215
216           err = svn_wc__conflict_read_text_conflict(&conflict_working, NULL, NULL,
217                                                     db, src_abspath, conflict,
218                                                     scratch_pool,
219                                                     scratch_pool);
220
221           if (err && err->apr_err == SVN_ERR_WC_MISSING)
222             {
223               /* not text conflicted */
224               svn_error_clear(err);
225               conflict_working = NULL;
226             }
227           else
228             SVN_ERR(err);
229
230           if (conflict_working)
231             {
232               svn_node_kind_t working_kind;
233
234               /* Does the ".mine" file exist? */
235               SVN_ERR(svn_io_check_path(conflict_working, &working_kind,
236                                         scratch_pool));
237
238               if (working_kind == svn_node_file)
239                 {
240                    /* Don't perform unmodified/pristine optimization */
241                   handle_as_unversioned = TRUE;
242                   my_src_abspath = conflict_working;
243                 }
244             }
245         }
246
247       SVN_ERR(copy_to_tmpdir(&work_items, NULL, db, my_src_abspath,
248                              dst_abspath, tmpdir_abspath,
249                              TRUE /* file_copy */,
250                              handle_as_unversioned /* unversioned */,
251                              cancel_func, cancel_baton,
252                              scratch_pool, scratch_pool));
253     }
254
255   /* Copy the (single) node's metadata, and move the new filesystem node
256      into place. */
257   SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath,
258                              dst_op_root_abspath, is_move, work_items,
259                              scratch_pool));
260
261   if (notify_func)
262     {
263       svn_wc_notify_t *notify
264         = svn_wc_create_notify(dst_abspath, svn_wc_notify_add,
265                                scratch_pool);
266       notify->kind = svn_node_file;
267
268       /* When we notify that we performed a copy, make sure we already did */
269       if (work_items != NULL)
270         SVN_ERR(svn_wc__wq_run(db, dst_abspath,
271                                cancel_func, cancel_baton, scratch_pool));
272       (*notify_func)(notify_baton, notify, scratch_pool);
273     }
274   return SVN_NO_ERROR;
275 }
276
277 /* Copy the versioned dir SRC_ABSPATH in DB to the path DST_ABSPATH in DB,
278    recursively.  If METADATA_ONLY is true, copy only the versioned metadata,
279    otherwise copy both the versioned metadata and the filesystem nodes (even
280    if they are the wrong kind, and including unversioned children).
281    If IS_MOVE is true, record move information in working copy meta
282    data in addition to copying the directory.
283
284    WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root)
285  */
286 static svn_error_t *
287 copy_versioned_dir(svn_wc__db_t *db,
288                    const char *src_abspath,
289                    const char *dst_abspath,
290                    const char *dst_op_root_abspath,
291                    const char *tmpdir_abspath,
292                    svn_boolean_t metadata_only,
293                    svn_boolean_t is_move,
294                    svn_cancel_func_t cancel_func,
295                    void *cancel_baton,
296                    svn_wc_notify_func2_t notify_func,
297                    void *notify_baton,
298                    apr_pool_t *scratch_pool)
299 {
300   svn_skel_t *work_items = NULL;
301   const char *dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
302   apr_hash_t *versioned_children;
303   apr_hash_t *conflicted_children;
304   apr_hash_t *disk_children;
305   apr_hash_index_t *hi;
306   svn_node_kind_t disk_kind;
307   apr_pool_t *iterpool;
308
309   /* Prepare a temp copy of the single filesystem node (usually a dir). */
310   if (!metadata_only)
311     {
312       SVN_ERR(copy_to_tmpdir(&work_items, &disk_kind,
313                              db, src_abspath, dst_abspath,
314                              tmpdir_abspath,
315                              FALSE /* file_copy */,
316                              FALSE /* unversioned */,
317                              cancel_func, cancel_baton,
318                              scratch_pool, scratch_pool));
319     }
320
321   /* Copy the (single) node's metadata, and move the new filesystem node
322      into place. */
323   SVN_ERR(svn_wc__db_op_copy(db, src_abspath, dst_abspath,
324                              dst_op_root_abspath, is_move, work_items,
325                              scratch_pool));
326
327   if (notify_func)
328     {
329       svn_wc_notify_t *notify
330         = svn_wc_create_notify(dst_abspath, svn_wc_notify_add,
331                                scratch_pool);
332       notify->kind = svn_node_dir;
333
334       /* When we notify that we performed a copy, make sure we already did */
335       if (work_items != NULL)
336         SVN_ERR(svn_wc__wq_run(db, dir_abspath,
337                                cancel_func, cancel_baton, scratch_pool));
338
339       (*notify_func)(notify_baton, notify, scratch_pool);
340     }
341
342   if (!metadata_only && disk_kind == svn_node_dir)
343     /* All filesystem children, versioned and unversioned.  We're only
344        interested in their names, so we can pass TRUE as the only_check_type
345        param. */
346     SVN_ERR(svn_io_get_dirents3(&disk_children, src_abspath, TRUE,
347                                 scratch_pool, scratch_pool));
348   else
349     disk_children = NULL;
350
351   /* Copy all the versioned children */
352   iterpool = svn_pool_create(scratch_pool);
353   SVN_ERR(svn_wc__db_read_children_info(&versioned_children,
354                                         &conflicted_children,
355                                         db, src_abspath,
356                                         scratch_pool, iterpool));
357   for (hi = apr_hash_first(scratch_pool, versioned_children);
358        hi;
359        hi = apr_hash_next(hi))
360     {
361       const char *child_name, *child_src_abspath, *child_dst_abspath;
362       struct svn_wc__db_info_t *info;
363
364       svn_pool_clear(iterpool);
365
366       if (cancel_func)
367         SVN_ERR(cancel_func(cancel_baton));
368
369       child_name = svn__apr_hash_index_key(hi);
370       info = svn__apr_hash_index_val(hi);
371       child_src_abspath = svn_dirent_join(src_abspath, child_name, iterpool);
372       child_dst_abspath = svn_dirent_join(dst_abspath, child_name, iterpool);
373
374       if (info->op_root)
375         SVN_ERR(svn_wc__db_op_copy_shadowed_layer(db,
376                                                   child_src_abspath,
377                                                   child_dst_abspath,
378                                                   is_move,
379                                                   scratch_pool));
380
381       if (info->status == svn_wc__db_status_normal
382           || info->status == svn_wc__db_status_added)
383         {
384           /* We have more work to do than just changing the DB */
385           if (info->kind == svn_node_file)
386             {
387               /* We should skip this node if this child is a file external
388                  (issues #3589, #4000) */
389               if (!info->file_external)
390                 SVN_ERR(copy_versioned_file(db,
391                                             child_src_abspath,
392                                             child_dst_abspath,
393                                             dst_op_root_abspath,
394                                             tmpdir_abspath,
395                                             metadata_only, info->conflicted,
396                                             is_move,
397                                             cancel_func, cancel_baton,
398                                             NULL, NULL,
399                                             iterpool));
400             }
401           else if (info->kind == svn_node_dir)
402             SVN_ERR(copy_versioned_dir(db,
403                                        child_src_abspath, child_dst_abspath,
404                                        dst_op_root_abspath, tmpdir_abspath,
405                                        metadata_only, is_move,
406                                        cancel_func, cancel_baton, NULL, NULL,
407                                        iterpool));
408           else
409             return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
410                                      _("cannot handle node kind for '%s'"),
411                                      svn_dirent_local_style(child_src_abspath,
412                                                             scratch_pool));
413         }
414       else if (info->status == svn_wc__db_status_deleted
415           || info->status == svn_wc__db_status_not_present
416           || info->status == svn_wc__db_status_excluded)
417         {
418           /* This will be copied as some kind of deletion. Don't touch
419              any actual files */
420           SVN_ERR(svn_wc__db_op_copy(db, child_src_abspath,
421                                      child_dst_abspath, dst_op_root_abspath,
422                                      is_move, NULL, iterpool));
423
424           /* Don't recurse on children while all we do is creating not-present
425              children */
426         }
427       else if (info->status == svn_wc__db_status_incomplete)
428         {
429           /* Should go ahead and copy incomplete to incomplete? Try to
430              copy as much as possible, or give up early? */
431           return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
432                                    _("Cannot handle status of '%s'"),
433                                    svn_dirent_local_style(child_src_abspath,
434                                                           iterpool));
435         }
436       else
437         {
438           SVN_ERR_ASSERT(info->status == svn_wc__db_status_server_excluded);
439
440           return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
441                                    _("Cannot copy '%s' excluded by server"),
442                                    svn_dirent_local_style(child_src_abspath,
443                                                           iterpool));
444         }
445
446       if (disk_children
447           && (info->status == svn_wc__db_status_normal
448               || info->status == svn_wc__db_status_added))
449         {
450           /* Remove versioned child as it has been handled */
451           svn_hash_sets(disk_children, child_name, NULL);
452         }
453     }
454
455   /* Copy the remaining filesystem children, which are unversioned, skipping
456      any conflict-marker files. */
457   if (disk_children && apr_hash_count(disk_children))
458     {
459       apr_hash_t *marker_files;
460
461       SVN_ERR(svn_wc__db_get_conflict_marker_files(&marker_files, db,
462                                                    src_abspath, scratch_pool,
463                                                    scratch_pool));
464
465       work_items = NULL;
466
467       for (hi = apr_hash_first(scratch_pool, disk_children); hi;
468            hi = apr_hash_next(hi))
469         {
470           const char *name = svn__apr_hash_index_key(hi);
471           const char *unver_src_abspath, *unver_dst_abspath;
472           svn_skel_t *work_item;
473
474           if (svn_wc_is_adm_dir(name, iterpool))
475             continue;
476
477           if (cancel_func)
478             SVN_ERR(cancel_func(cancel_baton));
479
480           svn_pool_clear(iterpool);
481           unver_src_abspath = svn_dirent_join(src_abspath, name, iterpool);
482           unver_dst_abspath = svn_dirent_join(dst_abspath, name, iterpool);
483
484           if (marker_files &&
485               svn_hash_gets(marker_files, unver_src_abspath))
486             continue;
487
488           SVN_ERR(copy_to_tmpdir(&work_item, NULL, db, unver_src_abspath,
489                                  unver_dst_abspath, tmpdir_abspath,
490                                  TRUE /* recursive */, TRUE /* unversioned */,
491                                  cancel_func, cancel_baton,
492                                  scratch_pool, iterpool));
493
494           if (work_item)
495             work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
496         }
497       SVN_ERR(svn_wc__db_wq_add(db, dst_abspath, work_items, iterpool));
498     }
499
500   svn_pool_destroy(iterpool);
501
502   return SVN_NO_ERROR;
503 }
504
505
506 /* The guts of svn_wc_copy3() and svn_wc_move().
507  * The additional parameter IS_MOVE indicates whether this is a copy or
508  * a move operation.
509  *
510  * If MOVE_DEGRADED_TO_COPY is not NULL and a move had to be degraded
511  * to a copy, then set *MOVE_DEGRADED_TO_COPY. */
512 static svn_error_t *
513 copy_or_move(svn_boolean_t *move_degraded_to_copy,
514              svn_wc_context_t *wc_ctx,
515              const char *src_abspath,
516              const char *dst_abspath,
517              svn_boolean_t metadata_only,
518              svn_boolean_t is_move,
519              svn_boolean_t allow_mixed_revisions,
520              svn_cancel_func_t cancel_func,
521              void *cancel_baton,
522              svn_wc_notify_func2_t notify_func,
523              void *notify_baton,
524              apr_pool_t *scratch_pool)
525 {
526   svn_wc__db_t *db = wc_ctx->db;
527   svn_node_kind_t src_db_kind;
528   const char *dstdir_abspath;
529   svn_boolean_t conflicted;
530   const char *tmpdir_abspath;
531   const char *src_wcroot_abspath;
532   const char *dst_wcroot_abspath;
533   svn_boolean_t within_one_wc;
534   svn_wc__db_status_t src_status;
535   svn_error_t *err;
536
537   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
538   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
539
540   dstdir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
541
542   /* Ensure DSTDIR_ABSPATH belongs to the same repository as SRC_ABSPATH;
543      throw an error if not. */
544   {
545     svn_wc__db_status_t dstdir_status;
546     const char *src_repos_root_url, *dst_repos_root_url;
547     const char *src_repos_uuid, *dst_repos_uuid;
548     const char *src_repos_relpath;
549
550     err = svn_wc__db_read_info(&src_status, &src_db_kind, NULL,
551                                &src_repos_relpath, &src_repos_root_url,
552                                &src_repos_uuid, NULL, NULL, NULL, NULL, NULL,
553                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
554                                NULL, &conflicted, NULL, NULL, NULL, NULL,
555                                NULL, NULL,
556                                db, src_abspath, scratch_pool, scratch_pool);
557
558     if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
559       {
560         /* Replicate old error code and text */
561         svn_error_clear(err);
562         return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
563                                  _("'%s' is not under version control"),
564                                  svn_dirent_local_style(src_abspath,
565                                                         scratch_pool));
566       }
567     else
568       SVN_ERR(err);
569
570     /* Do this now, as we know the right data is cached */
571     SVN_ERR(svn_wc__db_get_wcroot(&src_wcroot_abspath, db, src_abspath,
572                                   scratch_pool, scratch_pool));
573
574     switch (src_status)
575       {
576         case svn_wc__db_status_deleted:
577           return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
578                                    _("Deleted node '%s' can't be copied."),
579                                    svn_dirent_local_style(src_abspath,
580                                                           scratch_pool));
581
582         case svn_wc__db_status_excluded:
583         case svn_wc__db_status_server_excluded:
584         case svn_wc__db_status_not_present:
585           return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
586                                    _("The node '%s' was not found."),
587                                    svn_dirent_local_style(src_abspath,
588                                                           scratch_pool));
589         default:
590           break;
591       }
592
593      if (is_move && ! strcmp(src_abspath, src_wcroot_abspath))
594       {
595         return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
596                                  _("'%s' is the root of a working copy and "
597                                    "cannot be moved"),
598                                    svn_dirent_local_style(src_abspath,
599                                                           scratch_pool));
600       }
601     if (is_move && src_repos_relpath && !src_repos_relpath[0])
602       {
603         return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
604                                  _("'%s' represents the repository root "
605                                    "and cannot be moved"),
606                                  svn_dirent_local_style(src_abspath,
607                                                         scratch_pool));
608       }
609
610     err = svn_wc__db_read_info(&dstdir_status, NULL, NULL, NULL,
611                                &dst_repos_root_url, &dst_repos_uuid, NULL,
612                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
613                                NULL, NULL, NULL, NULL, NULL, NULL,
614                                NULL, NULL, NULL, NULL,
615                                NULL, NULL, NULL,
616                                db, dstdir_abspath,
617                                scratch_pool, scratch_pool);
618
619     if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
620       {
621         /* An unversioned destination directory exists on disk. */
622         svn_error_clear(err);
623         return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
624                                  _("'%s' is not under version control"),
625                                  svn_dirent_local_style(dstdir_abspath,
626                                                         scratch_pool));
627       }
628     else
629       SVN_ERR(err);
630
631     /* Do this now, as we know the right data is cached */
632     SVN_ERR(svn_wc__db_get_wcroot(&dst_wcroot_abspath, db, dstdir_abspath,
633                                   scratch_pool, scratch_pool));
634
635     if (!src_repos_root_url)
636       {
637         if (src_status == svn_wc__db_status_added)
638           SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
639                                            &src_repos_root_url,
640                                            &src_repos_uuid, NULL, NULL, NULL,
641                                            NULL,
642                                            db, src_abspath,
643                                            scratch_pool, scratch_pool));
644         else
645           /* If not added, the node must have a base or we can't copy */
646           SVN_ERR(svn_wc__db_scan_base_repos(NULL, &src_repos_root_url,
647                                              &src_repos_uuid,
648                                              db, src_abspath,
649                                              scratch_pool, scratch_pool));
650       }
651
652     if (!dst_repos_root_url)
653       {
654         if (dstdir_status == svn_wc__db_status_added)
655           SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
656                                            &dst_repos_root_url,
657                                            &dst_repos_uuid, NULL, NULL, NULL,
658                                            NULL,
659                                            db, dstdir_abspath,
660                                            scratch_pool, scratch_pool));
661         else
662           /* If not added, the node must have a base or we can't copy */
663           SVN_ERR(svn_wc__db_scan_base_repos(NULL, &dst_repos_root_url,
664                                              &dst_repos_uuid,
665                                              db, dstdir_abspath,
666                                              scratch_pool, scratch_pool));
667       }
668
669     if (strcmp(src_repos_root_url, dst_repos_root_url) != 0
670         || strcmp(src_repos_uuid, dst_repos_uuid) != 0)
671       return svn_error_createf(
672          SVN_ERR_WC_INVALID_SCHEDULE, NULL,
673          _("Cannot copy to '%s', as it is not from repository '%s'; "
674            "it is from '%s'"),
675          svn_dirent_local_style(dst_abspath, scratch_pool),
676          src_repos_root_url, dst_repos_root_url);
677
678     if (dstdir_status == svn_wc__db_status_deleted)
679       return svn_error_createf(
680          SVN_ERR_WC_INVALID_SCHEDULE, NULL,
681          _("Cannot copy to '%s' as it is scheduled for deletion"),
682          svn_dirent_local_style(dst_abspath, scratch_pool));
683          /* ### should report dstdir_abspath instead of dst_abspath? */
684   }
685
686   /* TODO(#2843): Rework the error report. */
687   /* Check if the copy target is missing or hidden and thus not exist on the
688      disk, before actually doing the file copy. */
689   {
690     svn_wc__db_status_t dst_status;
691
692     err = svn_wc__db_read_info(&dst_status, NULL, NULL, NULL, NULL, NULL,
693                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
694                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
695                                NULL, NULL, NULL, NULL, NULL,
696                                db, dst_abspath, scratch_pool, scratch_pool);
697
698     if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
699       return svn_error_trace(err);
700
701     svn_error_clear(err);
702
703     if (!err)
704       switch (dst_status)
705         {
706           case svn_wc__db_status_excluded:
707             return svn_error_createf(
708                      SVN_ERR_ENTRY_EXISTS, NULL,
709                      _("'%s' is already under version control "
710                        "but is excluded."),
711                      svn_dirent_local_style(dst_abspath, scratch_pool));
712           case svn_wc__db_status_server_excluded:
713             return svn_error_createf(
714                      SVN_ERR_ENTRY_EXISTS, NULL,
715                      _("'%s' is already under version control"),
716                      svn_dirent_local_style(dst_abspath, scratch_pool));
717
718           case svn_wc__db_status_deleted:
719           case svn_wc__db_status_not_present:
720             break; /* OK to add */
721
722           default:
723             return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
724                                _("There is already a versioned item '%s'"),
725                                svn_dirent_local_style(dst_abspath,
726                                                       scratch_pool));
727         }
728   }
729
730   /* Check that the target path is not obstructed, if required. */
731   if (!metadata_only)
732     {
733       svn_node_kind_t dst_kind;
734
735       /* (We need only to check the root of the copy, not every path inside
736          copy_versioned_file/_dir.) */
737       SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind, scratch_pool));
738       if (dst_kind != svn_node_none)
739         return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
740                                  _("'%s' already exists and is in the way"),
741                                  svn_dirent_local_style(dst_abspath,
742                                                         scratch_pool));
743     }
744
745   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
746                                          dstdir_abspath,
747                                          scratch_pool, scratch_pool));
748
749   within_one_wc = (strcmp(src_wcroot_abspath, dst_wcroot_abspath) == 0);
750
751   if (is_move
752       && !within_one_wc)
753     {
754       if (move_degraded_to_copy)
755         *move_degraded_to_copy = TRUE;
756
757       is_move = FALSE;
758     }
759
760   if (!within_one_wc)
761     SVN_ERR(svn_wc__db_pristine_transfer(db, src_abspath, dst_wcroot_abspath,
762                                          cancel_func, cancel_baton,
763                                          scratch_pool));
764
765   if (src_db_kind == svn_node_file
766       || src_db_kind == svn_node_symlink)
767     {
768       err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath,
769                                 tmpdir_abspath,
770                                 metadata_only, conflicted, is_move,
771                                 cancel_func, cancel_baton,
772                                 notify_func, notify_baton,
773                                 scratch_pool);
774     }
775   else
776     {
777       if (is_move
778           && src_status == svn_wc__db_status_normal)
779         {
780           svn_revnum_t min_rev;
781           svn_revnum_t max_rev;
782
783           /* Verify that the move source is a single-revision subtree. */
784           SVN_ERR(svn_wc__db_min_max_revisions(&min_rev, &max_rev, db,
785                                                src_abspath, FALSE, scratch_pool));
786           if (SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev) &&
787               min_rev != max_rev)
788             {
789               if (!allow_mixed_revisions)
790                 return svn_error_createf(SVN_ERR_WC_MIXED_REVISIONS, NULL,
791                                          _("Cannot move mixed-revision "
792                                            "subtree '%s' [%ld:%ld]; "
793                                            "try updating it first"),
794                                          svn_dirent_local_style(src_abspath,
795                                                                 scratch_pool),
796                                          min_rev, max_rev);
797
798               is_move = FALSE;
799               if (move_degraded_to_copy)
800                 *move_degraded_to_copy = TRUE;
801             }
802         }
803
804       err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath,
805                                tmpdir_abspath, metadata_only, is_move,
806                                cancel_func, cancel_baton,
807                                notify_func, notify_baton,
808                                scratch_pool);
809     }
810
811   if (err && svn_error_find_cause(err, SVN_ERR_CANCELLED))
812     return svn_error_trace(err);
813
814   if (is_move)
815     err = svn_error_compose_create(err,
816                 svn_wc__db_op_handle_move_back(NULL,
817                                                db, dst_abspath, src_abspath,
818                                                NULL /* work_items */,
819                                                scratch_pool));
820
821   /* Run the work queue with the remaining work */
822   SVN_ERR(svn_error_compose_create(
823                                 err,
824                                 svn_wc__wq_run(db, dst_abspath,
825                                                    cancel_func, cancel_baton,
826                                                    scratch_pool)));
827
828   return SVN_NO_ERROR;
829 }
830
831
832 /* Public Interface */
833
834 svn_error_t *
835 svn_wc_copy3(svn_wc_context_t *wc_ctx,
836              const char *src_abspath,
837              const char *dst_abspath,
838              svn_boolean_t metadata_only,
839              svn_cancel_func_t cancel_func,
840              void *cancel_baton,
841              svn_wc_notify_func2_t notify_func,
842              void *notify_baton,
843              apr_pool_t *scratch_pool)
844 {
845   /* Verify that we have the required write lock. */
846   SVN_ERR(svn_wc__write_check(wc_ctx->db,
847                               svn_dirent_dirname(dst_abspath, scratch_pool),
848                               scratch_pool));
849
850   return svn_error_trace(copy_or_move(NULL, wc_ctx, src_abspath, dst_abspath,
851                                       metadata_only, FALSE /* is_move */,
852                                       TRUE /* allow_mixed_revisions */,
853                                       cancel_func, cancel_baton,
854                                       notify_func, notify_baton,
855                                       scratch_pool));
856 }
857
858
859 /* Remove the conflict markers of NODE_ABSPATH, that were left over after
860    copying NODE_ABSPATH from SRC_ABSPATH.
861
862    Only use this function when you know what you're doing. This function
863    explicitly ignores some case insensitivity issues!
864
865    */
866 static svn_error_t *
867 remove_node_conflict_markers(svn_wc__db_t *db,
868                              const char *src_abspath,
869                              const char *node_abspath,
870                              apr_pool_t *scratch_pool)
871 {
872   svn_skel_t *conflict;
873
874   SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath,
875                                    scratch_pool, scratch_pool));
876
877   /* Do we have conflict markers that should be removed? */
878   if (conflict != NULL)
879     {
880       const apr_array_header_t *markers;
881       int i;
882       const char *src_dir = svn_dirent_dirname(src_abspath, scratch_pool);
883       const char *dst_dir = svn_dirent_dirname(node_abspath, scratch_pool);
884
885       SVN_ERR(svn_wc__conflict_read_markers(&markers, db, src_abspath,
886                                             conflict,
887                                             scratch_pool, scratch_pool));
888
889       /* No iterpool: Maximum number of possible conflict markers is 4 */
890       for (i = 0; markers && (i < markers->nelts); i++)
891         {
892           const char *marker_abspath;
893           const char *child_relpath;
894           const char *child_abpath;
895
896           marker_abspath = APR_ARRAY_IDX(markers, i, const char *);
897
898           child_relpath = svn_dirent_is_child(src_dir, marker_abspath, NULL);
899
900           if (child_relpath)
901             {
902               child_abpath = svn_dirent_join(dst_dir, child_relpath,
903                                              scratch_pool);
904
905               SVN_ERR(svn_io_remove_file2(child_abpath, TRUE, scratch_pool));
906             }
907         }
908     }
909
910   return SVN_NO_ERROR;
911 }
912
913 /* Remove all the conflict markers below SRC_DIR_ABSPATH, that were left over
914    after copying WC_DIR_ABSPATH from SRC_DIR_ABSPATH.
915
916    This function doesn't remove the conflict markers on WC_DIR_ABSPATH
917    itself!
918
919    Only use this function when you know what you're doing. This function
920    explicitly ignores some case insensitivity issues!
921    */
922 static svn_error_t *
923 remove_all_conflict_markers(svn_wc__db_t *db,
924                             const char *src_dir_abspath,
925                             const char *wc_dir_abspath,
926                             apr_pool_t *scratch_pool)
927 {
928   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
929   apr_hash_t *nodes;
930   apr_hash_t *conflicts; /* Unused */
931   apr_hash_index_t *hi;
932
933   /* Reuse a status helper to obtain all subdirs and conflicts in a single
934      db transaction. */
935   /* ### This uses a rifle to kill a fly. But at least it doesn't use heavy
936           artillery. */
937   SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db,
938                                         src_dir_abspath,
939                                         scratch_pool, iterpool));
940
941   for (hi = apr_hash_first(scratch_pool, nodes);
942        hi;
943        hi = apr_hash_next(hi))
944     {
945       const char *name = svn__apr_hash_index_key(hi);
946       struct svn_wc__db_info_t *info = svn__apr_hash_index_val(hi);
947
948       if (info->conflicted)
949         {
950           svn_pool_clear(iterpool);
951           SVN_ERR(remove_node_conflict_markers(
952                             db,
953                             svn_dirent_join(src_dir_abspath, name, iterpool),
954                             svn_dirent_join(wc_dir_abspath, name, iterpool),
955                             iterpool));
956         }
957       if (info->kind == svn_node_dir)
958         {
959           svn_pool_clear(iterpool);
960           SVN_ERR(remove_all_conflict_markers(
961                             db,
962                             svn_dirent_join(src_dir_abspath, name, iterpool),
963                             svn_dirent_join(wc_dir_abspath, name, iterpool),
964                             iterpool));
965         }
966     }
967
968   svn_pool_destroy(iterpool);
969   return SVN_NO_ERROR;
970 }
971
972 svn_error_t *
973 svn_wc__move2(svn_wc_context_t *wc_ctx,
974               const char *src_abspath,
975               const char *dst_abspath,
976               svn_boolean_t metadata_only,
977               svn_boolean_t allow_mixed_revisions,
978               svn_cancel_func_t cancel_func,
979               void *cancel_baton,
980               svn_wc_notify_func2_t notify_func,
981               void *notify_baton,
982               apr_pool_t *scratch_pool)
983 {
984   svn_wc__db_t *db = wc_ctx->db;
985   svn_boolean_t move_degraded_to_copy = FALSE;
986   svn_node_kind_t kind;
987   svn_boolean_t conflicted;
988
989   /* Verify that we have the required write locks. */
990   SVN_ERR(svn_wc__write_check(wc_ctx->db,
991                               svn_dirent_dirname(src_abspath, scratch_pool),
992                               scratch_pool));
993   SVN_ERR(svn_wc__write_check(wc_ctx->db,
994                               svn_dirent_dirname(dst_abspath, scratch_pool),
995                               scratch_pool));
996
997   SVN_ERR(copy_or_move(&move_degraded_to_copy,
998                        wc_ctx, src_abspath, dst_abspath,
999                        TRUE /* metadata_only */,
1000                        TRUE /* is_move */,
1001                        allow_mixed_revisions,
1002                        cancel_func, cancel_baton,
1003                        notify_func, notify_baton,
1004                        scratch_pool));
1005
1006   /* An interrupt at this point will leave the new copy marked as
1007      moved-here but the source has not yet been deleted or marked as
1008      moved-to. */
1009
1010   /* Should we be using a workqueue for this move?  It's not clear.
1011      What should happen if the copy above is interrupted?  The user
1012      may want to abort the move and a workqueue might interfere with
1013      that.
1014
1015      BH: On Windows it is not unlikely to encounter an access denied on
1016      this line. Installing the move in the workqueue via the copy_or_move
1017      might make it hard to recover from that situation, while the DB
1018      is still in a valid state. So be careful when switching this over
1019      to the workqueue. */
1020   if (!metadata_only)
1021     SVN_ERR(svn_io_file_rename(src_abspath, dst_abspath, scratch_pool));
1022
1023   SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
1024                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1025                                NULL, NULL, NULL, NULL, NULL, NULL,
1026                                &conflicted, NULL, NULL, NULL,
1027                                NULL, NULL, NULL,
1028                                db, src_abspath,
1029                                scratch_pool, scratch_pool));
1030
1031   if (kind == svn_node_dir)
1032     SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath,
1033                                         scratch_pool));
1034
1035   if (conflicted)
1036     SVN_ERR(remove_node_conflict_markers(db, src_abspath, dst_abspath,
1037                                          scratch_pool));
1038
1039   SVN_ERR(svn_wc__db_op_delete(db, src_abspath,
1040                                move_degraded_to_copy ? NULL : dst_abspath,
1041                                TRUE /* delete_dir_externals */,
1042                                NULL /* conflict */, NULL /* work_items */,
1043                                cancel_func, cancel_baton,
1044                                notify_func, notify_baton,
1045                                scratch_pool));
1046
1047   return SVN_NO_ERROR;
1048 }