2 * copy.c: wc 'copy' functionality.
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
26 #include "svn_pools.h"
27 #include "svn_error.h"
31 #include "adm_files.h"
34 #include "translate.h"
36 #include "svn_private_config.h"
37 #include "private/svn_wc_private.h"
42 /* Helper function for svn_wc_copy2() which handles WC->WC copying of
43 files which are scheduled for addition or unversioned.
45 Copy file SRC_PATH to DST_BASENAME in DST_PARENT_ACCESS.
47 DST_PARENT_ACCESS is a 0 depth locked access for a versioned directory
48 in the same WC as SRC_PATH.
50 If SRC_IS_ADDED is true then SRC_PATH is scheduled for addition and
51 DST_BASENAME will also be scheduled for addition.
53 If SRC_IS_ADDED is false then SRC_PATH is the unversioned child
54 file of a versioned or added parent and DST_BASENAME is simply copied.
56 Use POOL for all necessary allocations.
59 copy_added_file_administratively(const char *src_path
,
60 svn_boolean_t src_is_added
,
61 svn_wc_adm_access_t
*dst_parent_access
,
62 const char *dst_basename
,
63 svn_cancel_func_t cancel_func
,
65 svn_wc_notify_func2_t notify_func
,
70 = svn_path_join(svn_wc_adm_access_path(dst_parent_access
),
73 /* Copy this file and possibly put it under version control. */
74 SVN_ERR(svn_io_copy_file(src_path
, dst_path
, TRUE
, pool
));
78 SVN_ERR(svn_wc_add2(dst_path
, dst_parent_access
, NULL
,
79 SVN_INVALID_REVNUM
, cancel_func
,
80 cancel_baton
, notify_func
,
88 /* Helper function for svn_wc_copy2() which handles WC->WC copying of
89 directories which are scheduled for addition or unversioned.
91 Recursively copy directory SRC_PATH and its children, excluding
92 administrative directories, to DST_BASENAME in DST_PARENT_ACCESS.
94 DST_PARENT_ACCESS is a 0 depth locked access for a versioned directory
95 in the same WC as SRC_PATH.
97 SRC_ACCESS is a -1 depth access for SRC_PATH
99 If SRC_IS_ADDED is true then SRC_PATH is scheduled for addition and
100 DST_BASENAME will also be scheduled for addition.
102 If SRC_IS_ADDED is false then SRC_PATH is the unversioned child
103 directory of a versioned or added parent and DST_BASENAME is simply
106 Use POOL for all necessary allocations.
109 copy_added_dir_administratively(const char *src_path
,
110 svn_boolean_t src_is_added
,
111 svn_wc_adm_access_t
*dst_parent_access
,
112 svn_wc_adm_access_t
*src_access
,
113 const char *dst_basename
,
114 svn_cancel_func_t cancel_func
,
116 svn_wc_notify_func2_t notify_func
,
120 const char *dst_parent
= svn_wc_adm_access_path(dst_parent_access
);
124 /* src_path is the top of an unversioned tree, just copy
125 the whole thing and we are done. */
126 SVN_ERR(svn_io_copy_dir_recursively(src_path
, dst_parent
, dst_basename
,
127 TRUE
, cancel_func
, cancel_baton
,
132 const svn_wc_entry_t
*entry
;
133 svn_wc_adm_access_t
*dst_child_dir_access
;
134 svn_wc_adm_access_t
*src_child_dir_access
;
136 apr_finfo_t this_entry
;
139 apr_int32_t flags
= APR_FINFO_TYPE
| APR_FINFO_NAME
;
140 /* The 'dst_path' is simply dst_parent/dst_basename */
141 const char *dst_path
= svn_path_join(dst_parent
, dst_basename
, pool
);
143 /* Check cancellation; note that this catches recursive calls too. */
145 SVN_ERR(cancel_func(cancel_baton
));
147 /* "Copy" the dir dst_path and schedule it, and possibly
148 its children, for addition. */
149 SVN_ERR(svn_io_dir_make(dst_path
, APR_OS_DEFAULT
, pool
));
151 /* Add the directory, adding locking access for dst_path
152 to dst_parent_access at the same time. */
153 SVN_ERR(svn_wc_add2(dst_path
, dst_parent_access
, NULL
,
154 SVN_INVALID_REVNUM
, cancel_func
, cancel_baton
,
155 notify_func
, notify_baton
, pool
));
157 /* Get the accesses for the newly added dir and its source, we'll
158 need both to process any of SRC_PATHS's children below. */
159 SVN_ERR(svn_wc_adm_retrieve(&dst_child_dir_access
, dst_parent_access
,
161 SVN_ERR(svn_wc_adm_retrieve(&src_child_dir_access
, src_access
,
164 SVN_ERR(svn_io_dir_open(&dir
, src_path
, pool
));
166 subpool
= svn_pool_create(pool
);
168 /* Read src_path's entries one by one. */
171 const char *src_fullpath
;
173 svn_pool_clear(subpool
);
175 err
= svn_io_dir_read(&this_entry
, flags
, dir
, subpool
);
179 /* Check if we're done reading the dir's entries. */
180 if (APR_STATUS_IS_ENOENT(err
->apr_err
))
182 apr_status_t apr_err
;
184 svn_error_clear(err
);
185 apr_err
= apr_dir_close(dir
);
187 return svn_error_wrap_apr(apr_err
,
190 svn_path_local_style(src_path
,
196 return svn_error_createf(err
->apr_err
, err
,
197 _("Error during recursive copy "
199 svn_path_local_style(src_path
,
204 /* Skip entries for this dir and its parent. */
205 if (this_entry
.name
[0] == '.'
206 && (this_entry
.name
[1] == '\0'
207 || (this_entry
.name
[1] == '.'
208 && this_entry
.name
[2] == '\0')))
211 /* Check cancellation so you can cancel during an
212 * add of a directory with lots of files. */
214 SVN_ERR(cancel_func(cancel_baton
));
216 /* Skip over SVN admin directories. */
217 if (svn_wc_is_adm_dir(this_entry
.name
, subpool
))
220 /* Construct the full path of the entry. */
221 src_fullpath
= svn_path_join(src_path
, this_entry
.name
, subpool
);
223 SVN_ERR(svn_wc_entry(&entry
, src_fullpath
, src_child_dir_access
,
226 /* Recurse on directories; add files; ignore the rest. */
227 if (this_entry
.filetype
== APR_DIR
)
229 SVN_ERR(copy_added_dir_administratively(src_fullpath
,
230 entry
? TRUE
: FALSE
,
231 dst_child_dir_access
,
232 src_child_dir_access
,
240 else if (this_entry
.filetype
!= APR_UNKFILE
)
242 SVN_ERR(copy_added_file_administratively(src_fullpath
,
243 entry
? TRUE
: FALSE
,
244 dst_child_dir_access
,
253 } /* End while(1) loop */
255 svn_pool_destroy(subpool
);
257 } /* End else src_is_added. */
263 /* Helper function for copy_file_administratively() and
264 copy_dir_administratively(). Determines the COPYFROM_URL and
265 COPYFROM_REV of a file or directory SRC_PATH which is the descendant
266 of an explicitly moved or copied directory that has not been committed.
269 get_copyfrom_url_rev_via_parent(const char *src_path
,
270 const char **copyfrom_url
,
271 svn_revnum_t
*copyfrom_rev
,
272 svn_wc_adm_access_t
*src_access
,
275 const char *parent_path
= svn_path_dirname(src_path
, pool
);
276 const char *rest
= svn_path_basename(src_path
, pool
);
277 *copyfrom_url
= NULL
;
279 while (! *copyfrom_url
)
281 svn_wc_adm_access_t
*parent_access
;
282 const svn_wc_entry_t
*entry
;
284 /* Don't look for parent_path in src_access if it can't be
286 if (svn_path_is_ancestor(svn_wc_adm_access_path(src_access
),
289 SVN_ERR(svn_wc_adm_retrieve(&parent_access
, src_access
,
291 SVN_ERR(svn_wc__entry_versioned(&entry
, parent_path
, parent_access
,
294 else /* ...get access for parent_path instead. */
296 SVN_ERR(svn_wc_adm_probe_open3(&parent_access
, NULL
,
297 parent_path
, FALSE
, -1,
299 SVN_ERR(svn_wc__entry_versioned(&entry
, parent_path
, parent_access
,
301 SVN_ERR(svn_wc_adm_close(parent_access
));
304 if (entry
->copyfrom_url
)
306 *copyfrom_url
= svn_path_join(entry
->copyfrom_url
, rest
,
308 *copyfrom_rev
= entry
->copyfrom_rev
;
312 rest
= svn_path_join(svn_path_basename(parent_path
, pool
),
314 parent_path
= svn_path_dirname(parent_path
, pool
);
321 /* A helper for copy_file_administratively() which sets *COPYFROM_URL
322 and *COPYFROM_REV appropriately (possibly to NULL/SVN_INVALID_REVNUM).
323 DST_ENTRY may be NULL. */
324 static APR_INLINE svn_error_t
*
325 determine_copyfrom_info(const char **copyfrom_url
, svn_revnum_t
*copyfrom_rev
,
326 const char *src_path
, svn_wc_adm_access_t
*src_access
,
327 const svn_wc_entry_t
*src_entry
,
328 const svn_wc_entry_t
*dst_entry
, apr_pool_t
*pool
)
333 if (src_entry
->copyfrom_url
)
335 /* When copying/moving a file that was already explicitly
336 copied/moved then we know the URL it was copied from... */
337 url
= src_entry
->copyfrom_url
;
338 rev
= src_entry
->copyfrom_rev
;
342 /* ...But if this file is merely the descendant of an explicitly
343 copied/moved directory, we need to do a bit more work to
344 determine copyfrom_url and copyfrom_rev. */
345 SVN_ERR(get_copyfrom_url_rev_via_parent(src_path
, &url
, &rev
,
349 if (dst_entry
&& rev
== dst_entry
->revision
&&
350 strcmp(url
, dst_entry
->url
) == 0)
352 /* Suppress copyfrom info when the copy source is the same as
353 for the destination. */
355 rev
= SVN_INVALID_REVNUM
;
357 else if (src_entry
->copyfrom_url
)
359 /* As the URL was allocated for src_entry, make a copy. */
360 url
= apr_pstrdup(pool
, url
);
368 /* This function effectively creates and schedules a file for
369 addition, but does extra administrative things to allow it to
370 function as a 'copy'.
374 - src_path points to a file under version control
375 - dst_parent points to a dir under version control, in the same
377 - dst_basename will be the 'new' name of the copied file in dst_parent
380 copy_file_administratively(const char *src_path
,
381 svn_wc_adm_access_t
*src_access
,
382 svn_wc_adm_access_t
*dst_parent
,
383 const char *dst_basename
,
384 svn_wc_notify_func2_t notify_copied
,
388 svn_node_kind_t dst_kind
;
389 const svn_wc_entry_t
*src_entry
, *dst_entry
;
391 /* The 'dst_path' is simply dst_parent/dst_basename */
393 = svn_path_join(svn_wc_adm_access_path(dst_parent
), dst_basename
, pool
);
395 /* Discover the paths to the two text-base files */
396 const char *src_txtb
= svn_wc__text_base_path(src_path
, FALSE
, pool
);
397 const char *tmp_txtb
= svn_wc__text_base_path(dst_path
, TRUE
, pool
);
399 /* Sanity check: if dst file exists already, don't allow overwrite. */
400 SVN_ERR(svn_io_check_path(dst_path
, &dst_kind
, pool
));
401 if (dst_kind
!= svn_node_none
)
402 return svn_error_createf(SVN_ERR_ENTRY_EXISTS
, NULL
,
403 _("'%s' already exists and is in the way"),
404 svn_path_local_style(dst_path
, pool
));
406 /* Even if DST_PATH doesn't exist it may still be a versioned file; it
407 may be scheduled for deletion, or the user may simply have removed the
408 working copy. Since we are going to write to DST_PATH text-base and
409 prop-base we need to detect such cases and abort. */
410 SVN_ERR(svn_wc_entry(&dst_entry
, dst_path
, dst_parent
, FALSE
, pool
));
411 if (dst_entry
&& dst_entry
->kind
== svn_node_file
)
413 if (dst_entry
->schedule
!= svn_wc_schedule_delete
)
414 return svn_error_createf(SVN_ERR_ENTRY_EXISTS
, NULL
,
415 _("There is already a versioned item '%s'"),
416 svn_path_local_style(dst_path
, pool
));
419 /* Sanity check 1: You cannot make a copy of something that's not
420 under version control. */
421 SVN_ERR(svn_wc__entry_versioned(&src_entry
, src_path
, src_access
, FALSE
,
424 /* Sanity check 2: You cannot make a copy of something that's not
425 in the repository unless it's a copy of an uncommitted copy. */
426 if ((src_entry
->schedule
== svn_wc_schedule_add
&& (! src_entry
->copied
))
427 || (! src_entry
->url
))
428 return svn_error_createf
429 (SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
430 _("Cannot copy or move '%s': it is not in the repository yet; "
431 "try committing first"),
432 svn_path_local_style(src_path
, pool
));
435 /* Schedule the new file for addition in its parent, WITH HISTORY. */
437 const char *copyfrom_url
;
438 const char *tmp_wc_text
;
439 svn_revnum_t copyfrom_rev
;
440 apr_hash_t
*props
, *base_props
;
442 /* Are we moving or copying a file that is already moved or copied
443 but not committed? */
444 if (src_entry
->copied
)
446 SVN_ERR(determine_copyfrom_info(©from_url
, ©from_rev
, src_path
,
447 src_access
, src_entry
, dst_entry
,
452 /* Grrr. Why isn't the first arg to svn_wc_get_ancestry const? */
455 SVN_ERR(svn_wc_get_ancestry(&tmp
, ©from_rev
, src_path
, src_access
,
461 /* Load source base and working props. */
462 SVN_ERR(svn_wc__load_props(&base_props
, &props
, NULL
, src_access
,
465 /* Copy pristine text-base to temporary location. */
466 SVN_ERR(svn_io_copy_file(src_txtb
, tmp_txtb
, TRUE
, pool
));
468 /* Copy working copy file to temporary location */
470 svn_boolean_t special
;
472 SVN_ERR(svn_wc_create_tmp_file2(NULL
, &tmp_wc_text
,
473 svn_wc_adm_access_path(dst_parent
),
474 svn_io_file_del_none
, pool
));
476 SVN_ERR(svn_wc__get_special(&special
, src_path
, src_access
, pool
));
479 SVN_ERR(svn_subst_copy_and_translate3(src_path
, tmp_wc_text
,
481 FALSE
, special
, pool
));
484 SVN_ERR(svn_io_copy_file(src_path
, tmp_wc_text
, TRUE
, pool
));
487 SVN_ERR(svn_wc_add_repos_file2(dst_path
, dst_parent
,
488 tmp_txtb
, tmp_wc_text
,
490 copyfrom_url
, copyfrom_rev
, pool
));
493 /* Report the addition to the caller. */
494 if (notify_copied
!= NULL
)
496 svn_wc_notify_t
*notify
= svn_wc_create_notify(dst_path
,
499 notify
->kind
= svn_node_file
;
500 (*notify_copied
)(notify_baton
, notify
, pool
);
507 /* Recursively crawl over a directory PATH and do a number of things:
510 - Convert deleted items to schedule-delete items
511 - Set .svn directories to be hidden
514 post_copy_cleanup(svn_wc_adm_access_t
*adm_access
,
517 apr_pool_t
*subpool
= svn_pool_create(pool
);
519 apr_hash_index_t
*hi
;
520 svn_wc_entry_t
*entry
;
521 const char *path
= svn_wc_adm_access_path(adm_access
);
523 /* Remove wcprops. */
524 SVN_ERR(svn_wc__props_delete(path
, svn_wc__props_wcprop
, adm_access
, pool
));
526 /* Read this directory's entries file. */
527 SVN_ERR(svn_wc_entries_read(&entries
, adm_access
, FALSE
, pool
));
529 /* Because svn_io_copy_dir_recursively() doesn't copy directory
530 permissions, we'll patch up our tree's .svn subdirs to be
532 #ifdef APR_FILE_ATTR_HIDDEN
534 const char *adm_dir
= svn_wc__adm_path(path
, FALSE
, pool
, NULL
);
535 const char *path_apr
;
537 SVN_ERR(svn_path_cstring_from_utf8(&path_apr
, adm_dir
, pool
));
538 status
= apr_file_attrs_set(path_apr
,
539 APR_FILE_ATTR_HIDDEN
,
540 APR_FILE_ATTR_HIDDEN
,
543 return svn_error_wrap_apr(status
, _("Can't hide directory '%s'"),
544 svn_path_local_style(adm_dir
, pool
));
548 /* Loop over all children, removing lock tokens and recursing into
550 SVN_ERR(svn_wc_entries_read(&entries
, adm_access
, TRUE
, pool
));
551 for (hi
= apr_hash_first(pool
, entries
); hi
; hi
= apr_hash_next(hi
))
555 svn_node_kind_t kind
;
556 svn_boolean_t deleted
= FALSE
;
557 apr_uint64_t flags
= SVN_WC__ENTRY_MODIFY_FORCE
;
559 svn_pool_clear(subpool
);
561 apr_hash_this(hi
, &key
, NULL
, &val
);
564 deleted
= entry
->deleted
;
566 /* Convert deleted="true" into schedule="delete" for all
567 children (and grandchildren, if RECURSE is set) of the path
568 represented by ADM_ACCESS. The result of this is that when
569 the copy is committed the items in question get deleted and
570 the result is a directory in the repository that matches the
571 original source directory for copy. If this were not done
572 the deleted="true" items would simply vanish from the entries
573 file as the copy is added to the working copy. The new
574 schedule="delete" files do not have a text-base and so their
575 scheduled deletion cannot be reverted. For directories a
576 placeholder with an svn_node_kind_t of svn_node_file and
577 schedule="delete" is used to avoid the problems associated
578 with creating a directory. See Issue #2101 for details. */
581 entry
->schedule
= svn_wc_schedule_delete
;
582 flags
|= SVN_WC__ENTRY_MODIFY_SCHEDULE
;
584 entry
->deleted
= FALSE
;
585 flags
|= SVN_WC__ENTRY_MODIFY_DELETED
;
587 if (entry
->kind
== svn_node_dir
)
589 /* ### WARNING: Very dodgy stuff here! ###
591 Directories are a problem since a schedule delete directory
592 needs an admin directory to be present. It's possible to
593 create a dummy admin directory and that sort of works, it's
594 good enough if the user commits the copy. Where it falls
595 down is if the user *reverts* the dummy directory since the
596 now schedule normal, copied, directory doesn't have the
599 The dodgy solution is to cheat and use a schedule delete file
600 as a placeholder! This is sufficient to provide a delete
601 when the copy is committed. Attempts to revert any such
602 "fake" files will fail due to a missing text-base. This
603 effectively means that the schedule deletes have to remain
604 schedule delete until the copy is committed, when they become
605 state deleted and everything works! */
606 entry
->kind
= svn_node_file
;
607 flags
|= SVN_WC__ENTRY_MODIFY_KIND
;
611 /* Remove lock stuffs. */
612 if (entry
->lock_token
)
614 entry
->lock_token
= NULL
;
615 entry
->lock_owner
= NULL
;
616 entry
->lock_comment
= NULL
;
617 entry
->lock_creation_date
= 0;
618 flags
|= (SVN_WC__ENTRY_MODIFY_LOCK_TOKEN
619 | SVN_WC__ENTRY_MODIFY_LOCK_OWNER
620 | SVN_WC__ENTRY_MODIFY_LOCK_COMMENT
621 | SVN_WC__ENTRY_MODIFY_LOCK_CREATION_DATE
);
624 /* If we meaningfully modified the flags, we must be wanting to
626 if (flags
!= SVN_WC__ENTRY_MODIFY_FORCE
)
627 SVN_ERR(svn_wc__entry_modify(adm_access
, key
, entry
,
628 flags
, TRUE
, subpool
));
630 /* If a dir, not deleted, and not "this dir", recurse. */
632 && (kind
== svn_node_dir
)
633 && (strcmp(key
, SVN_WC_ENTRY_THIS_DIR
) != 0))
635 svn_wc_adm_access_t
*child_access
;
636 const char *child_path
;
637 child_path
= svn_path_join
638 (svn_wc_adm_access_path(adm_access
), key
, subpool
);
639 SVN_ERR(svn_wc_adm_retrieve(&child_access
, adm_access
,
640 child_path
, subpool
));
641 SVN_ERR(post_copy_cleanup(child_access
, subpool
));
646 svn_pool_destroy(subpool
);
652 /* This function effectively creates and schedules a dir for
653 addition, but does extra administrative things to allow it to
654 function as a 'copy'.
658 - src_path points to a dir under version control
659 - dst_parent points to a dir under version control, in the same
661 - dst_basename will be the 'new' name of the copied dir in dst_parent
664 copy_dir_administratively(const char *src_path
,
665 svn_wc_adm_access_t
*src_access
,
666 svn_wc_adm_access_t
*dst_parent
,
667 const char *dst_basename
,
668 svn_cancel_func_t cancel_func
,
670 svn_wc_notify_func2_t notify_copied
,
674 const svn_wc_entry_t
*src_entry
;
675 svn_wc_adm_access_t
*adm_access
;
677 /* The 'dst_path' is simply dst_parent/dst_basename */
678 const char *dst_path
= svn_path_join(svn_wc_adm_access_path(dst_parent
),
681 /* Sanity check 1: You cannot make a copy of something that's not
682 under version control. */
683 SVN_ERR(svn_wc__entry_versioned(&src_entry
, src_path
, src_access
, FALSE
,
686 /* Sanity check 2: You cannot make a copy of something that's not
687 in the repository unless it's a copy of an uncommitted copy. */
688 if ((src_entry
->schedule
== svn_wc_schedule_add
&& (! src_entry
->copied
))
689 || (! src_entry
->url
))
690 return svn_error_createf
691 (SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
692 _("Cannot copy or move '%s': it is not in the repository yet; "
693 "try committing first"),
694 svn_path_local_style(src_path
, pool
));
696 /* Recursively copy the whole directory over. This gets us all
697 text-base, props, base-props, as well as entries, local mods,
698 schedulings, existences, etc.
700 ### Should we be copying unversioned items within the directory? */
701 SVN_ERR(svn_io_copy_dir_recursively(src_path
,
702 svn_wc_adm_access_path(dst_parent
),
705 cancel_func
, cancel_baton
,
708 /* If this is part of a move, the copied directory will be locked,
709 because the source directory was locked. Running cleanup will remove
710 the locks, even though this directory has not yet been added to the
712 SVN_ERR(svn_wc_cleanup2(dst_path
, NULL
, cancel_func
, cancel_baton
, pool
));
714 /* We've got some post-copy cleanup to do now. */
715 SVN_ERR(svn_wc_adm_open3(&adm_access
, NULL
, dst_path
, TRUE
, -1,
716 cancel_func
, cancel_baton
, pool
));
717 SVN_ERR(post_copy_cleanup(adm_access
, pool
));
719 /* Schedule the directory for addition in both its parent and itself
720 (this_dir) -- WITH HISTORY. This function should leave the
721 existing administrative dir untouched. */
723 const char *copyfrom_url
;
724 svn_revnum_t copyfrom_rev
;
725 svn_wc_entry_t tmp_entry
;
727 /* Are we copying a dir that is already copied but not committed? */
728 if (src_entry
->copied
)
730 const svn_wc_entry_t
*dst_entry
;
731 SVN_ERR(svn_wc_entry(&dst_entry
, dst_path
, dst_parent
, FALSE
, pool
));
732 SVN_ERR(determine_copyfrom_info(©from_url
, ©from_rev
, src_path
,
733 src_access
, src_entry
, dst_entry
,
736 /* The URL for a copied dir won't exist in the repository, which
737 will cause svn_wc_add2() below to fail. Set the URL to the
738 URL of the first copy for now to prevent this. */
739 tmp_entry
.url
= apr_pstrdup(pool
, copyfrom_url
);
740 SVN_ERR(svn_wc__entry_modify(adm_access
, NULL
, /* This Dir */
742 SVN_WC__ENTRY_MODIFY_URL
, TRUE
,
747 /* Grrr. Why isn't the first arg to svn_wc_get_ancestry const? */
750 SVN_ERR(svn_wc_get_ancestry(&tmp
, ©from_rev
, src_path
, src_access
,
756 SVN_ERR(svn_wc_adm_close(adm_access
));
758 SVN_ERR(svn_wc_add2(dst_path
, dst_parent
,
759 copyfrom_url
, copyfrom_rev
,
760 cancel_func
, cancel_baton
,
761 notify_copied
, notify_baton
, pool
));
769 /* Public Interface */
772 svn_wc_copy2(const char *src_path
,
773 svn_wc_adm_access_t
*dst_parent
,
774 const char *dst_basename
,
775 svn_cancel_func_t cancel_func
,
777 svn_wc_notify_func2_t notify_func
,
781 svn_wc_adm_access_t
*adm_access
;
782 svn_node_kind_t src_kind
;
783 const char *dst_path
;
784 const svn_wc_entry_t
*dst_entry
, *src_entry
;
786 SVN_ERR(svn_wc_adm_probe_open3(&adm_access
, NULL
, src_path
, FALSE
, -1,
787 cancel_func
, cancel_baton
, pool
));
789 dst_path
= svn_wc_adm_access_path(dst_parent
);
790 SVN_ERR(svn_wc__entry_versioned(&dst_entry
, dst_path
, dst_parent
, FALSE
,
792 SVN_ERR(svn_wc__entry_versioned(&src_entry
, src_path
, adm_access
, FALSE
,
795 if ((src_entry
->repos
!= NULL
&& dst_entry
->repos
!= NULL
) &&
796 strcmp(src_entry
->repos
, dst_entry
->repos
) != 0)
797 return svn_error_createf
798 (SVN_ERR_WC_INVALID_SCHEDULE
, NULL
,
799 _("Cannot copy to '%s', as it is not from repository '%s'; "
801 svn_path_local_style(svn_wc_adm_access_path(dst_parent
), pool
),
802 src_entry
->repos
, dst_entry
->repos
);
803 if (dst_entry
->schedule
== svn_wc_schedule_delete
)
804 return svn_error_createf
805 (SVN_ERR_WC_INVALID_SCHEDULE
, NULL
,
806 _("Cannot copy to '%s' as it is scheduled for deletion"),
807 svn_path_local_style(svn_wc_adm_access_path(dst_parent
), pool
));
809 SVN_ERR(svn_io_check_path(src_path
, &src_kind
, pool
));
811 if (src_kind
== svn_node_file
)
813 /* Check if we are copying a file scheduled for addition,
814 these require special handling. */
815 if (src_entry
->schedule
== svn_wc_schedule_add
816 && (! src_entry
->copied
))
818 SVN_ERR(copy_added_file_administratively(src_path
, TRUE
,
819 dst_parent
, dst_basename
,
820 cancel_func
, cancel_baton
,
821 notify_func
, notify_baton
,
826 SVN_ERR(copy_file_administratively(src_path
, adm_access
,
827 dst_parent
, dst_basename
,
828 notify_func
, notify_baton
, pool
));
831 else if (src_kind
== svn_node_dir
)
833 /* Check if we are copying a directory scheduled for addition,
834 these require special handling. */
835 if (src_entry
->schedule
== svn_wc_schedule_add
836 && (! src_entry
->copied
))
838 SVN_ERR(copy_added_dir_administratively(src_path
, TRUE
,
839 dst_parent
, adm_access
,
840 dst_basename
, cancel_func
,
841 cancel_baton
, notify_func
,
842 notify_baton
, pool
));
846 SVN_ERR(copy_dir_administratively(src_path
, adm_access
,
847 dst_parent
, dst_basename
,
848 cancel_func
, cancel_baton
,
849 notify_func
, notify_baton
, pool
));
853 SVN_ERR(svn_wc_adm_close(adm_access
));
861 svn_wc_copy(const char *src_path
,
862 svn_wc_adm_access_t
*dst_parent
,
863 const char *dst_basename
,
864 svn_cancel_func_t cancel_func
,
866 svn_wc_notify_func_t notify_func
,
870 svn_wc__compat_notify_baton_t nb
;
872 nb
.func
= notify_func
;
873 nb
.baton
= notify_baton
;
875 return svn_wc_copy2(src_path
, dst_parent
, dst_basename
, cancel_func
,
876 cancel_baton
, svn_wc__compat_call_notify_func
,