2 * export.c: export a tree.
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 /* ==================================================================== */
25 #include <apr_file_io.h>
27 #include "svn_types.h"
28 #include "svn_client.h"
29 #include "svn_string.h"
30 #include "svn_error.h"
32 #include "svn_pools.h"
33 #include "svn_subst.h"
36 #include "svn_props.h"
39 #include "svn_private_config.h"
40 #include "private/svn_wc_private.h"
45 /* Add EXTERNALS_PROP_VAL for the export destination path PATH to
48 add_externals(apr_hash_t
*externals
,
50 const svn_string_t
*externals_prop_val
)
52 apr_pool_t
*pool
= apr_hash_pool_get(externals
);
54 if (! externals_prop_val
)
57 apr_hash_set(externals
,
58 apr_pstrdup(pool
, path
),
60 apr_pstrmemdup(pool
, externals_prop_val
->data
,
61 externals_prop_val
->len
));
64 /* Helper function that gets the eol style and optionally overrides the
65 EOL marker for files marked as native with the EOL marker matching
66 the string specified in requested_value which is of the same format
67 as the svn:eol-style property values. */
69 get_eol_style(svn_subst_eol_style_t
*style
,
72 const char *requested_value
)
74 svn_subst_eol_style_from_value(style
, eol
, value
);
75 if (requested_value
&& *style
== svn_subst_eol_style_native
)
77 svn_subst_eol_style_t requested_style
;
78 const char *requested_eol
;
80 svn_subst_eol_style_from_value(&requested_style
, &requested_eol
,
83 if (requested_style
== svn_subst_eol_style_fixed
)
86 return svn_error_createf(SVN_ERR_IO_UNKNOWN_EOL
, NULL
,
87 _("'%s' is not a valid EOL value"),
94 copy_one_versioned_file(const char *from
,
96 svn_wc_adm_access_t
*adm_access
,
97 svn_opt_revision_t
*revision
,
98 const char *native_eol
,
101 const svn_wc_entry_t
*entry
;
102 apr_hash_t
*kw
= NULL
;
103 svn_subst_eol_style_t style
;
106 svn_string_t
*eol_style
, *keywords
, *executable
, *externals
, *special
;
107 const char *eol
= NULL
;
108 svn_boolean_t local_mod
= FALSE
;
111 SVN_ERR(svn_wc_entry(&entry
, from
, adm_access
, FALSE
, pool
));
113 /* Only export 'added' files when the revision is WORKING.
114 Otherwise, skip the 'added' files, since they didn't exist
115 in the BASE revision and don't have an associated text-base.
117 Don't export 'deleted' files and directories unless it's a
118 revision other than WORKING. These files and directories
119 don't really exist in WORKING. */
120 if ((revision
->kind
!= svn_opt_revision_working
&&
121 entry
->schedule
== svn_wc_schedule_add
) ||
122 (revision
->kind
== svn_opt_revision_working
&&
123 entry
->schedule
== svn_wc_schedule_delete
))
126 if (revision
->kind
!= svn_opt_revision_working
)
128 SVN_ERR(svn_wc_get_pristine_copy_path(from
, &base
,
130 SVN_ERR(svn_wc_get_prop_diffs(NULL
, &props
, from
,
135 svn_wc_status2_t
*status
;
138 SVN_ERR(svn_wc_prop_list(&props
, from
,
140 SVN_ERR(svn_wc_status2(&status
, from
,
142 if (status
->text_status
!= svn_wc_status_normal
)
146 eol_style
= apr_hash_get(props
, SVN_PROP_EOL_STYLE
,
147 APR_HASH_KEY_STRING
);
148 keywords
= apr_hash_get(props
, SVN_PROP_KEYWORDS
,
149 APR_HASH_KEY_STRING
);
150 executable
= apr_hash_get(props
, SVN_PROP_EXECUTABLE
,
151 APR_HASH_KEY_STRING
);
152 externals
= apr_hash_get(props
, SVN_PROP_EXTERNALS
,
153 APR_HASH_KEY_STRING
);
154 special
= apr_hash_get(props
, SVN_PROP_SPECIAL
,
155 APR_HASH_KEY_STRING
);
158 SVN_ERR(get_eol_style(&style
, &eol
, eol_style
->data
, native_eol
));
160 if (local_mod
&& (! special
))
162 /* Use the modified time from the working copy of
164 SVN_ERR(svn_io_file_affected_time(&tm
, from
, pool
));
168 tm
= entry
->cmt_date
;
178 /* For locally modified files, we'll append an 'M'
179 to the revision number, and set the author to
180 "(local)" since we can't always determine the
181 current user's username */
183 author
= _("(local)");
188 author
= entry
->cmt_author
;
191 SVN_ERR(svn_subst_build_keywords2
192 (&kw
, keywords
->data
,
193 apr_psprintf(pool
, fmt
, entry
->cmt_rev
),
194 entry
->url
, tm
, author
, pool
));
197 SVN_ERR(svn_subst_copy_and_translate3(base
, to
, eol
, FALSE
,
199 special
? TRUE
: FALSE
,
202 SVN_ERR(svn_io_set_file_executable(to
, TRUE
,
206 SVN_ERR(svn_io_set_file_affected_time(tm
, to
, pool
));
212 copy_versioned_files(const char *from
,
214 svn_opt_revision_t
*revision
,
216 svn_boolean_t ignore_externals
,
218 const char *native_eol
,
219 svn_client_ctx_t
*ctx
,
222 svn_wc_adm_access_t
*adm_access
;
223 const svn_wc_entry_t
*entry
;
225 apr_pool_t
*iterpool
;
227 apr_hash_index_t
*hi
;
230 SVN_ERR(svn_wc_adm_probe_open3(&adm_access
, NULL
, from
, FALSE
,
231 0, ctx
->cancel_func
, ctx
->cancel_baton
,
234 SVN_ERR(svn_wc__entry_versioned(&entry
, from
, adm_access
, FALSE
, pool
));
236 /* Only export 'added' files when the revision is WORKING.
237 Otherwise, skip the 'added' files, since they didn't exist
238 in the BASE revision and don't have an associated text-base.
240 Don't export 'deleted' files and directories unless it's a
241 revision other than WORKING. These files and directories
242 don't really exist in WORKING. */
243 if ((revision
->kind
!= svn_opt_revision_working
&&
244 entry
->schedule
== svn_wc_schedule_add
) ||
245 (revision
->kind
== svn_opt_revision_working
&&
246 entry
->schedule
== svn_wc_schedule_delete
))
249 if (entry
->kind
== svn_node_dir
)
251 /* Try to make the new directory. If this fails because the
252 directory already exists, check our FORCE flag to see if we
254 SVN_ERR(svn_io_stat(&finfo
, from
, APR_FINFO_PROT
, pool
));
255 err
= svn_io_dir_make(to
, finfo
.protection
, pool
);
258 if (! APR_STATUS_IS_EEXIST(err
->apr_err
))
261 SVN_ERR_W(err
, _("Destination directory exists, and will not be "
262 "overwritten unless forced"));
264 svn_error_clear(err
);
267 SVN_ERR(svn_wc_entries_read(&entries
, adm_access
, FALSE
, pool
));
269 iterpool
= svn_pool_create(pool
);
270 for (hi
= apr_hash_first(pool
, entries
); hi
; hi
= apr_hash_next(hi
))
276 svn_pool_clear(iterpool
);
278 apr_hash_this(hi
, &key
, NULL
, &val
);
283 if (ctx
->cancel_func
)
284 SVN_ERR(ctx
->cancel_func(ctx
->cancel_baton
));
286 /* ### We could also invoke ctx->notify_func somewhere in
287 ### here... Is it called for, though? Not sure. */
289 if (entry
->kind
== svn_node_dir
)
291 if (strcmp(item
, SVN_WC_ENTRY_THIS_DIR
) == 0)
293 ; /* skip this, it's the current directory that we're
298 if (depth
== svn_depth_infinity
)
300 const char *new_from
= svn_path_join(from
, item
,
302 const char *new_to
= svn_path_join(to
, item
, iterpool
);
304 SVN_ERR(copy_versioned_files(new_from
, new_to
,
306 ignore_externals
, depth
,
312 else if (entry
->kind
== svn_node_file
)
314 const char *new_from
= svn_path_join(from
, item
, iterpool
);
315 const char *new_to
= svn_path_join(to
, item
, iterpool
);
317 SVN_ERR(copy_one_versioned_file(new_from
, new_to
, adm_access
,
318 revision
, native_eol
,
323 /* Handle externals. */
324 if (! ignore_externals
&& depth
== svn_depth_infinity
325 && entry
->depth
== svn_depth_infinity
)
327 apr_array_header_t
*ext_items
;
328 const svn_string_t
*prop_val
;
330 SVN_ERR(svn_wc_prop_get(&prop_val
, SVN_PROP_EXTERNALS
,
331 from
, adm_access
, pool
));
332 if (prop_val
!= NULL
)
336 SVN_ERR(svn_wc_parse_externals_description3(&ext_items
, from
,
339 for (i
= 0; i
< ext_items
->nelts
; ++i
)
341 svn_wc_external_item2_t
*ext_item
;
342 const char *new_from
, *new_to
;
344 svn_pool_clear(iterpool
);
346 ext_item
= APR_ARRAY_IDX(ext_items
, i
,
347 svn_wc_external_item2_t
*);
348 new_from
= svn_path_join(from
, ext_item
->target_dir
,
350 new_to
= svn_path_join(to
, ext_item
->target_dir
,
353 /* The target dir might have multiple components. Guarantee
354 the path leading down to the last component. */
355 if (svn_path_component_count(ext_item
->target_dir
) > 1)
357 const char *parent
= svn_path_dirname(new_to
, iterpool
);
358 SVN_ERR(svn_io_make_dir_recursively(parent
, iterpool
));
361 SVN_ERR(copy_versioned_files(new_from
, new_to
,
362 revision
, force
, FALSE
,
363 svn_depth_infinity
, native_eol
,
369 svn_pool_destroy(iterpool
);
371 else if (entry
->kind
== svn_node_file
)
373 SVN_ERR(copy_one_versioned_file(from
, to
, adm_access
, revision
,
377 SVN_ERR(svn_wc_adm_close(adm_access
));
382 /* Abstraction of open_root.
384 * Create PATH if it does not exist and is not obstructed, and invoke
385 * NOTIFY_FUNC with NOTIFY_BATON on PATH.
387 * If PATH exists but is a file, then error with SVN_ERR_WC_NOT_DIRECTORY.
389 * If PATH is a already a directory, then error with
390 * SVN_ERR_WC_OBSTRUCTED_UPDATE, unless FORCE, in which case just
391 * export into PATH with no error.
394 open_root_internal(const char *path
,
396 svn_wc_notify_func2_t notify_func
,
400 svn_node_kind_t kind
;
402 SVN_ERR(svn_io_check_path(path
, &kind
, pool
));
403 if (kind
== svn_node_none
)
404 SVN_ERR(svn_io_make_dir_recursively(path
, pool
));
405 else if (kind
== svn_node_file
)
406 return svn_error_createf(SVN_ERR_WC_NOT_DIRECTORY
, NULL
,
407 _("'%s' exists and is not a directory"),
408 svn_path_local_style(path
, pool
));
409 else if ((kind
!= svn_node_dir
) || (! force
))
410 return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE
, NULL
,
411 _("'%s' already exists"),
412 svn_path_local_style(path
, pool
));
416 svn_wc_notify_t
*notify
= svn_wc_create_notify(path
,
417 svn_wc_notify_update_add
,
419 notify
->kind
= svn_node_dir
;
420 (*notify_func
)(notify_baton
, notify
, pool
);
427 /* ---------------------------------------------------------------------- */
429 /*** A dedicated 'export' editor, which does no .svn/ accounting. ***/
434 const char *root_path
;
435 const char *root_url
;
437 svn_revnum_t
*target_revision
;
438 apr_hash_t
*externals
;
439 const char *native_eol
;
441 svn_wc_notify_func2_t notify_func
;
448 struct edit_baton
*edit_baton
;
455 struct edit_baton
*edit_baton
;
460 /* We need to keep this around so we can explicitly close it in close_file,
461 thus flushing its output to disk so we can copy and translate it. */
462 apr_file_t
*tmp_file
;
464 /* The MD5 digest of the file's fulltext. This is all zeros until
465 the last textdelta window handler call returns. */
466 unsigned char text_digest
[APR_MD5_DIGESTSIZE
];
468 /* The three svn: properties we might actually care about. */
469 const svn_string_t
*eol_style_val
;
470 const svn_string_t
*keywords_val
;
471 const svn_string_t
*executable_val
;
472 svn_boolean_t special
;
474 /* Any keyword vals to be substituted */
475 const char *revision
;
480 /* Pool associated with this baton. */
487 svn_txdelta_window_handler_t apply_handler
;
495 set_target_revision(void *edit_baton
,
496 svn_revnum_t target_revision
,
499 struct edit_baton
*eb
= edit_baton
;
501 /* Stashing a target_revision in the baton */
502 *(eb
->target_revision
) = target_revision
;
508 /* Just ensure that the main export directory exists. */
510 open_root(void *edit_baton
,
511 svn_revnum_t base_revision
,
515 struct edit_baton
*eb
= edit_baton
;
516 struct dir_baton
*db
= apr_pcalloc(pool
, sizeof(*db
));
518 SVN_ERR(open_root_internal(eb
->root_path
, eb
->force
,
519 eb
->notify_func
, eb
->notify_baton
, pool
));
521 /* Build our dir baton. */
522 db
->path
= eb
->root_path
;
530 /* Ensure the directory exists, and send feedback. */
532 add_directory(const char *path
,
534 const char *copyfrom_path
,
535 svn_revnum_t copyfrom_revision
,
539 struct dir_baton
*pb
= parent_baton
;
540 struct dir_baton
*db
= apr_pcalloc(pool
, sizeof(*db
));
541 struct edit_baton
*eb
= pb
->edit_baton
;
542 const char *full_path
= svn_path_join(eb
->root_path
, path
, pool
);
543 svn_node_kind_t kind
;
545 SVN_ERR(svn_io_check_path(full_path
, &kind
, pool
));
546 if (kind
== svn_node_none
)
547 SVN_ERR(svn_io_dir_make(full_path
, APR_OS_DEFAULT
, pool
));
548 else if (kind
== svn_node_file
)
549 return svn_error_createf(SVN_ERR_WC_NOT_DIRECTORY
, NULL
,
550 _("'%s' exists and is not a directory"),
551 svn_path_local_style(full_path
, pool
));
552 else if (! (kind
== svn_node_dir
&& eb
->force
))
553 return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE
, NULL
,
554 _("'%s' already exists"),
555 svn_path_local_style(full_path
, pool
));
559 svn_wc_notify_t
*notify
= svn_wc_create_notify(full_path
,
560 svn_wc_notify_update_add
,
562 notify
->kind
= svn_node_dir
;
563 (*eb
->notify_func
)(eb
->notify_baton
, notify
, pool
);
566 /* Build our dir baton. */
567 db
->path
= full_path
;
575 /* Build a file baton. */
577 add_file(const char *path
,
579 const char *copyfrom_path
,
580 svn_revnum_t copyfrom_revision
,
584 struct dir_baton
*pb
= parent_baton
;
585 struct edit_baton
*eb
= pb
->edit_baton
;
586 struct file_baton
*fb
= apr_pcalloc(pool
, sizeof(*fb
));
587 const char *full_path
= svn_path_join(eb
->root_path
, path
, pool
);
588 const char *full_url
= svn_path_join(eb
->root_url
, path
, pool
);
591 fb
->path
= full_path
;
601 window_handler(svn_txdelta_window_t
*window
, void *baton
)
603 struct handler_baton
*hb
= baton
;
606 err
= hb
->apply_handler(window
, hb
->apply_baton
);
609 /* We failed to apply the patch; clean up the temporary file. */
610 apr_file_remove(hb
->tmppath
, hb
->pool
);
618 /* Write incoming data into the tmpfile stream */
620 apply_textdelta(void *file_baton
,
621 const char *base_checksum
,
623 svn_txdelta_window_handler_t
*handler
,
624 void **handler_baton
)
626 struct file_baton
*fb
= file_baton
;
627 struct handler_baton
*hb
= apr_palloc(pool
, sizeof(*hb
));
629 SVN_ERR(svn_io_open_unique_file2(&fb
->tmp_file
, &(fb
->tmppath
),
631 svn_io_file_del_none
, fb
->pool
));
634 hb
->tmppath
= fb
->tmppath
;
636 svn_txdelta_apply(svn_stream_empty(pool
),
637 svn_stream_from_aprfile(fb
->tmp_file
, pool
),
638 fb
->text_digest
, NULL
, pool
,
639 &hb
->apply_handler
, &hb
->apply_baton
);
642 *handler
= window_handler
;
648 change_file_prop(void *file_baton
,
650 const svn_string_t
*value
,
653 struct file_baton
*fb
= file_baton
;
658 /* Store only the magic three properties. */
659 if (strcmp(name
, SVN_PROP_EOL_STYLE
) == 0)
660 fb
->eol_style_val
= svn_string_dup(value
, fb
->pool
);
662 else if (strcmp(name
, SVN_PROP_KEYWORDS
) == 0)
663 fb
->keywords_val
= svn_string_dup(value
, fb
->pool
);
665 else if (strcmp(name
, SVN_PROP_EXECUTABLE
) == 0)
666 fb
->executable_val
= svn_string_dup(value
, fb
->pool
);
668 /* Try to fill out the baton's keywords-structure too. */
669 else if (strcmp(name
, SVN_PROP_ENTRY_COMMITTED_REV
) == 0)
670 fb
->revision
= apr_pstrdup(fb
->pool
, value
->data
);
672 else if (strcmp(name
, SVN_PROP_ENTRY_COMMITTED_DATE
) == 0)
673 SVN_ERR(svn_time_from_cstring(&fb
->date
, value
->data
, fb
->pool
));
675 else if (strcmp(name
, SVN_PROP_ENTRY_LAST_AUTHOR
) == 0)
676 fb
->author
= apr_pstrdup(fb
->pool
, value
->data
);
678 else if (strcmp(name
, SVN_PROP_SPECIAL
) == 0)
686 change_dir_prop(void *dir_baton
,
688 const svn_string_t
*value
,
691 struct dir_baton
*db
= dir_baton
;
692 struct edit_baton
*eb
= db
->edit_baton
;
694 if (value
&& (strcmp(name
, SVN_PROP_EXTERNALS
) == 0))
695 add_externals(eb
->externals
, db
->path
, value
);
701 /* Move the tmpfile to file, and send feedback. */
703 close_file(void *file_baton
,
704 const char *text_checksum
,
707 struct file_baton
*fb
= file_baton
;
708 struct edit_baton
*eb
= fb
->edit_baton
;
710 /* Was a txdelta even sent? */
714 SVN_ERR(svn_io_file_close(fb
->tmp_file
, fb
->pool
));
718 const char *actual_checksum
719 = svn_md5_digest_to_cstring(fb
->text_digest
, pool
);
721 if (actual_checksum
&& (strcmp(text_checksum
, actual_checksum
) != 0))
723 return svn_error_createf
724 (SVN_ERR_CHECKSUM_MISMATCH
, NULL
,
725 _("Checksum mismatch for '%s'; expected: '%s', actual: '%s'"),
726 svn_path_local_style(fb
->path
, pool
),
727 text_checksum
, actual_checksum
);
731 if ((! fb
->eol_style_val
) && (! fb
->keywords_val
) && (! fb
->special
))
733 SVN_ERR(svn_io_file_rename(fb
->tmppath
, fb
->path
, pool
));
737 svn_subst_eol_style_t style
;
739 apr_hash_t
*final_kw
;
741 if (fb
->eol_style_val
)
742 SVN_ERR(get_eol_style(&style
, &eol
, fb
->eol_style_val
->data
,
745 if (fb
->keywords_val
)
746 SVN_ERR(svn_subst_build_keywords2(&final_kw
, fb
->keywords_val
->data
,
747 fb
->revision
, fb
->url
, fb
->date
,
750 SVN_ERR(svn_subst_copy_and_translate3
751 (fb
->tmppath
, fb
->path
,
752 fb
->eol_style_val
? eol
: NULL
,
753 fb
->eol_style_val
? TRUE
: FALSE
, /* repair */
754 fb
->keywords_val
? final_kw
: NULL
,
759 SVN_ERR(svn_io_remove_file(fb
->tmppath
, pool
));
762 if (fb
->executable_val
)
763 SVN_ERR(svn_io_set_file_executable(fb
->path
, TRUE
, FALSE
, pool
));
765 if (fb
->date
&& (! fb
->special
))
766 SVN_ERR(svn_io_set_file_affected_time(fb
->date
, fb
->path
, pool
));
768 if (fb
->edit_baton
->notify_func
)
770 svn_wc_notify_t
*notify
= svn_wc_create_notify(fb
->path
,
771 svn_wc_notify_update_add
,
773 notify
->kind
= svn_node_file
;
774 (*fb
->edit_baton
->notify_func
)(fb
->edit_baton
->notify_baton
, notify
,
783 /*** Public Interfaces ***/
786 svn_client_export4(svn_revnum_t
*result_rev
,
789 const svn_opt_revision_t
*peg_revision
,
790 const svn_opt_revision_t
*revision
,
791 svn_boolean_t overwrite
,
792 svn_boolean_t ignore_externals
,
794 const char *native_eol
,
795 svn_client_ctx_t
*ctx
,
798 svn_revnum_t edit_revision
= SVN_INVALID_REVNUM
;
801 if (svn_path_is_url(from
) ||
802 ! (revision
->kind
== svn_opt_revision_base
||
803 revision
->kind
== svn_opt_revision_committed
||
804 revision
->kind
== svn_opt_revision_working
||
805 revision
->kind
== svn_opt_revision_unspecified
))
808 svn_ra_session_t
*ra_session
;
809 svn_node_kind_t kind
;
810 struct edit_baton
*eb
= apr_pcalloc(pool
, sizeof(*eb
));
811 const char *repos_root_url
;
813 /* Get the RA connection. */
814 SVN_ERR(svn_client__ra_session_from_path(&ra_session
, &revnum
,
817 revision
, ctx
, pool
));
819 /* Get the repository root. */
820 SVN_ERR(svn_ra_get_repos_root(ra_session
, &repos_root_url
, pool
));
824 eb
->force
= overwrite
;
825 eb
->target_revision
= &edit_revision
;
826 eb
->notify_func
= ctx
->notify_func2
;
827 eb
->notify_baton
= ctx
->notify_baton2
;
828 eb
->externals
= apr_hash_make(pool
);
829 eb
->native_eol
= native_eol
;
831 SVN_ERR(svn_ra_check_path(ra_session
, "", revnum
, &kind
, pool
));
833 if (kind
== svn_node_file
)
836 apr_hash_index_t
*hi
;
837 struct file_baton
*fb
= apr_pcalloc(pool
, sizeof(*fb
));
839 /* Since you cannot actually root an editor at a file, we
840 * manually drive a few functions of our editor. */
842 /* This is the equivalent of a parentless add_file(). */
844 fb
->path
= eb
->root_path
;
845 fb
->url
= eb
->root_url
;
848 /* Copied from apply_textdelta(). */
849 SVN_ERR(svn_io_open_unique_file2(&fb
->tmp_file
, &(fb
->tmppath
),
851 svn_io_file_del_none
, fb
->pool
));
853 /* Step outside the editor-likeness for a moment, to actually talk
854 * to the repository. */
855 SVN_ERR(svn_ra_get_file(ra_session
, "", revnum
,
856 svn_stream_from_aprfile(fb
->tmp_file
,
858 NULL
, &props
, pool
));
860 /* Push the props into change_file_prop(), to update the file_baton
861 * with information. */
862 for (hi
= apr_hash_first(pool
, props
); hi
; hi
= apr_hash_next(hi
))
866 apr_hash_this(hi
, &key
, NULL
, &val
);
867 SVN_ERR(change_file_prop(fb
, key
, val
, pool
));
870 /* And now just use close_file() to do all the keyword and EOL
871 * work, and put the file into place. */
872 SVN_ERR(close_file(fb
, NULL
, pool
));
874 else if (kind
== svn_node_dir
)
877 const svn_delta_editor_t
*export_editor
;
878 const svn_ra_reporter3_t
*reporter
;
880 svn_delta_editor_t
*editor
= svn_delta_default_editor(pool
);
881 svn_boolean_t use_sleep
= FALSE
;
883 editor
->set_target_revision
= set_target_revision
;
884 editor
->open_root
= open_root
;
885 editor
->add_directory
= add_directory
;
886 editor
->add_file
= add_file
;
887 editor
->apply_textdelta
= apply_textdelta
;
888 editor
->close_file
= close_file
;
889 editor
->change_file_prop
= change_file_prop
;
890 editor
->change_dir_prop
= change_dir_prop
;
892 SVN_ERR(svn_delta_get_cancellation_editor(ctx
->cancel_func
,
901 /* Manufacture a basic 'report' to the update reporter. */
902 SVN_ERR(svn_ra_do_update2(ra_session
,
903 &reporter
, &report_baton
,
905 "", /* no sub-target */
907 FALSE
, /* don't want copyfrom-args */
908 export_editor
, edit_baton
, pool
));
910 SVN_ERR(reporter
->set_path(report_baton
, "", revnum
,
911 /* Depth is irrelevant, as we're
912 passing start_empty=TRUE anyway. */
914 TRUE
, /* "help, my dir is empty!" */
917 SVN_ERR(reporter
->finish_report(report_baton
, pool
));
919 /* Special case: Due to our sly export/checkout method of
920 * updating an empty directory, no target will have been created
921 * if the exported item is itself an empty directory
922 * (export_editor->open_root never gets called, because there
923 * are no "changes" to make to the empty dir we reported to the
926 * So we just create the empty dir manually; but we do it via
927 * open_root_internal(), in order to get proper notification.
929 SVN_ERR(svn_io_check_path(to
, &kind
, pool
));
930 if (kind
== svn_node_none
)
931 SVN_ERR(open_root_internal
932 (to
, overwrite
, ctx
->notify_func2
,
933 ctx
->notify_baton2
, pool
));
935 if (! ignore_externals
&& depth
== svn_depth_infinity
)
936 SVN_ERR(svn_client__fetch_externals(eb
->externals
, from
, to
,
937 repos_root_url
, depth
, TRUE
,
938 &use_sleep
, ctx
, pool
));
940 else if (kind
== svn_node_none
)
942 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL
, NULL
,
943 _("URL '%s' doesn't exist"), from
);
945 /* kind == svn_node_unknown not handled */
949 svn_opt_revision_t working_revision
= *revision
;
950 /* This is a working copy export. */
951 if (working_revision
.kind
== svn_opt_revision_unspecified
)
953 /* Default to WORKING in the case that we have
954 been given a working copy path */
955 working_revision
.kind
= svn_opt_revision_working
;
958 /* just copy the contents of the working copy into the target path. */
959 SVN_ERR(copy_versioned_files(from
, to
, &working_revision
, overwrite
,
960 ignore_externals
, depth
, native_eol
,
965 if (ctx
->notify_func2
)
967 svn_wc_notify_t
*notify
968 = svn_wc_create_notify(to
,
969 svn_wc_notify_update_completed
, pool
);
970 notify
->revision
= edit_revision
;
971 (*ctx
->notify_func2
)(ctx
->notify_baton2
, notify
, pool
);
975 *result_rev
= edit_revision
;
981 svn_client_export3(svn_revnum_t
*result_rev
,
984 const svn_opt_revision_t
*peg_revision
,
985 const svn_opt_revision_t
*revision
,
986 svn_boolean_t overwrite
,
987 svn_boolean_t ignore_externals
,
988 svn_boolean_t recurse
,
989 const char *native_eol
,
990 svn_client_ctx_t
*ctx
,
993 return svn_client_export4(result_rev
, from
, to
, peg_revision
, revision
,
994 overwrite
, ignore_externals
,
995 SVN_DEPTH_INFINITY_OR_FILES(recurse
),
996 native_eol
, ctx
, pool
);
1000 svn_client_export2(svn_revnum_t
*result_rev
,
1003 svn_opt_revision_t
*revision
,
1004 svn_boolean_t force
,
1005 const char *native_eol
,
1006 svn_client_ctx_t
*ctx
,
1009 svn_opt_revision_t peg_revision
;
1011 peg_revision
.kind
= svn_opt_revision_unspecified
;
1013 return svn_client_export3(result_rev
, from
, to
, &peg_revision
,
1014 revision
, force
, FALSE
, TRUE
,
1015 native_eol
, ctx
, pool
);
1020 svn_client_export(svn_revnum_t
*result_rev
,
1023 svn_opt_revision_t
*revision
,
1024 svn_boolean_t force
,
1025 svn_client_ctx_t
*ctx
,
1028 return svn_client_export2(result_rev
, from
, to
, revision
, force
, NULL
, ctx
,