2 * import.c: wrappers around import functionality.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 /* ==================================================================== */
31 #include <apr_strings.h>
37 #include "svn_delta.h"
38 #include "svn_subst.h"
39 #include "svn_client.h"
40 #include "svn_string.h"
41 #include "svn_pools.h"
42 #include "svn_error_codes.h"
43 #include "svn_dirent_uri.h"
46 #include "svn_sorts.h"
47 #include "svn_props.h"
50 #include "private/svn_subr_private.h"
51 #include "private/svn_ra_private.h"
52 #include "private/svn_magic.h"
54 #include "svn_private_config.h"
56 /* Import context baton.
58 ### TODO: Add the following items to this baton:
59 /` import editor/baton. `/
60 const svn_delta_editor_t *editor;
63 /` Client context baton `/
64 svn_client_ctx_t `ctx;
66 /` Paths (keys) excluded from the import (values ignored) `/
69 typedef struct import_ctx_t
71 /* Whether any changes were made to the repository */
72 svn_boolean_t repos_changed;
74 /* A magic cookie for mime-type detection. */
75 svn_magic__cookie_t *magic_cookie;
77 /* Collection of all possible configuration file dictated auto-props and
78 svn:auto-props. A hash mapping const char * file patterns to a
79 second hash which maps const char * property names to const char *
80 property values. Properties which don't have a value, e.g.
81 svn:executable, simply map the property name to an empty string.
82 May be NULL if autoprops are disabled. */
83 apr_hash_t *autoprops;
87 /* Apply LOCAL_ABSPATH's contents (as a delta against the empty string) to
88 FILE_BATON in EDITOR. Use POOL for any temporary allocation.
89 PROPERTIES is the set of node properties set on this file.
91 Fill DIGEST with the md5 checksum of the sent file; DIGEST must be
92 at least APR_MD5_DIGESTSIZE bytes long. */
94 /* ### how does this compare against svn_wc_transmit_text_deltas2() ??? */
97 send_file_contents(const char *local_abspath,
99 const svn_delta_editor_t *editor,
100 apr_hash_t *properties,
101 unsigned char *digest,
104 svn_stream_t *contents;
105 svn_txdelta_window_handler_t handler;
107 const svn_string_t *eol_style_val = NULL, *keywords_val = NULL;
108 svn_boolean_t special = FALSE;
109 svn_subst_eol_style_t eol_style;
111 apr_hash_t *keywords;
113 /* If there are properties, look for EOL-style and keywords ones. */
116 eol_style_val = apr_hash_get(properties, SVN_PROP_EOL_STYLE,
117 sizeof(SVN_PROP_EOL_STYLE) - 1);
118 keywords_val = apr_hash_get(properties, SVN_PROP_KEYWORDS,
119 sizeof(SVN_PROP_KEYWORDS) - 1);
120 if (svn_hash_gets(properties, SVN_PROP_SPECIAL))
124 /* Get an editor func that wants to consume the delta stream. */
125 SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool,
126 &handler, &handler_baton));
129 svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data);
133 eol_style = svn_subst_eol_style_none;
137 SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data,
138 APR_STRINGIFY(SVN_INVALID_REVNUM),
139 "", "", 0, "", pool));
145 SVN_ERR(svn_subst_read_specialfile(&contents, local_abspath,
150 /* Open the working copy file. */
151 SVN_ERR(svn_stream_open_readonly(&contents, local_abspath, pool, pool));
153 /* If we have EOL styles or keywords, then detranslate the file. */
154 if (svn_subst_translation_required(eol_style, eol, keywords,
157 if (eol_style == svn_subst_eol_style_unknown)
158 return svn_error_createf(SVN_ERR_IO_UNKNOWN_EOL, NULL,
159 _("%s property on '%s' contains "
160 "unrecognized EOL-style '%s'"),
162 svn_dirent_local_style(local_abspath,
164 eol_style_val->data);
166 /* We're importing, so translate files with 'native' eol-style to
167 * repository-normal form, not to this platform's native EOL. */
168 if (eol_style == svn_subst_eol_style_native)
169 eol = SVN_SUBST_NATIVE_EOL_STR;
171 /* Wrap the working copy stream with a filter to detranslate it. */
172 contents = svn_subst_stream_translated(contents,
181 /* Send the file's contents to the delta-window handler. */
182 return svn_error_trace(svn_txdelta_send_stream(contents, handler,
183 handler_baton, digest,
188 /* Import file PATH as EDIT_PATH in the repository directory indicated
189 * by DIR_BATON in EDITOR.
191 * Accumulate file paths and their batons in FILES, which must be
192 * non-null. (These are used to send postfix textdeltas later).
194 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON
197 * Use POOL for any temporary allocation.
200 import_file(const svn_delta_editor_t *editor,
202 const char *local_abspath,
203 const char *edit_path,
204 const svn_io_dirent2_t *dirent,
205 import_ctx_t *import_ctx,
206 svn_client_ctx_t *ctx,
210 const char *mimetype = NULL;
211 unsigned char digest[APR_MD5_DIGESTSIZE];
212 const char *text_checksum;
213 apr_hash_t* properties;
214 apr_hash_index_t *hi;
216 SVN_ERR(svn_path_check_valid(local_abspath, pool));
218 /* Add the file, using the pool from the FILES hash. */
219 SVN_ERR(editor->add_file(edit_path, dir_baton, NULL, SVN_INVALID_REVNUM,
222 /* Remember that the repository was modified */
223 import_ctx->repos_changed = TRUE;
225 if (! dirent->special)
227 /* add automatic properties */
228 SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype,
230 import_ctx->magic_cookie,
231 import_ctx->autoprops,
235 properties = apr_hash_make(pool);
239 for (hi = apr_hash_first(pool, properties); hi; hi = apr_hash_next(hi))
241 const char *pname = svn__apr_hash_index_key(hi);
242 const svn_string_t *pval = svn__apr_hash_index_val(hi);
244 SVN_ERR(editor->change_file_prop(file_baton, pname, pval, pool));
248 if (ctx->notify_func2)
250 svn_wc_notify_t *notify
251 = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added,
253 notify->kind = svn_node_file;
254 notify->mime_type = mimetype;
255 notify->content_state = notify->prop_state
256 = svn_wc_notify_state_inapplicable;
257 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
258 (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
261 /* If this is a special file, we need to set the svn:special
262 property and create a temporary detranslated version in order to
263 send to the server. */
266 svn_hash_sets(properties, SVN_PROP_SPECIAL,
267 svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool));
268 SVN_ERR(editor->change_file_prop(file_baton, SVN_PROP_SPECIAL,
269 svn_hash_gets(properties,
274 /* Now, transmit the file contents. */
275 SVN_ERR(send_file_contents(local_abspath, file_baton, editor,
276 properties, digest, pool));
278 /* Finally, close the file. */
280 svn_checksum_to_cstring(svn_checksum__from_digest_md5(digest, pool), pool);
282 return editor->close_file(file_baton, text_checksum, pool);
286 /* Return in CHILDREN a mapping of basenames to dirents for the importable
287 * children of DIR_ABSPATH. EXCLUDES is a hash of absolute paths to filter
288 * out. IGNORES and GLOBAL_IGNORES, if non-NULL, are lists of basename
289 * patterns to filter out.
290 * FILTER_CALLBACK and FILTER_BATON will be called for each absolute path,
291 * allowing users to further filter the list of returned entries.
293 * Results are returned in RESULT_POOL; use SCRATCH_POOL for temporary data.*/
295 get_filtered_children(apr_hash_t **children,
296 const char *dir_abspath,
297 apr_hash_t *excludes,
298 apr_array_header_t *ignores,
299 apr_array_header_t *global_ignores,
300 svn_client_import_filter_func_t filter_callback,
302 svn_client_ctx_t *ctx,
303 apr_pool_t *result_pool,
304 apr_pool_t *scratch_pool)
307 apr_hash_index_t *hi;
308 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
310 SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, result_pool,
313 for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
315 const char *base_name = svn__apr_hash_index_key(hi);
316 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
317 const char *local_abspath;
319 svn_pool_clear(iterpool);
321 local_abspath = svn_dirent_join(dir_abspath, base_name, iterpool);
323 if (svn_wc_is_adm_dir(base_name, iterpool))
325 /* If someone's trying to import a directory named the same
326 as our administrative directories, that's probably not
327 what they wanted to do. If they are importing a file
328 with that name, something is bound to blow up when they
329 checkout what they've imported. So, just skip items with
331 if (ctx->notify_func2)
333 svn_wc_notify_t *notify
334 = svn_wc_create_notify(svn_dirent_join(local_abspath, base_name,
336 svn_wc_notify_skip, iterpool);
337 notify->kind = svn_node_dir;
338 notify->content_state = notify->prop_state
339 = svn_wc_notify_state_inapplicable;
340 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
341 (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
344 svn_hash_sets(dirents, base_name, NULL);
347 /* If this is an excluded path, exclude it. */
348 if (svn_hash_gets(excludes, local_abspath))
350 svn_hash_sets(dirents, base_name, NULL);
354 if (ignores && svn_wc_match_ignore_list(base_name, ignores, iterpool))
356 svn_hash_sets(dirents, base_name, NULL);
360 if (global_ignores &&
361 svn_wc_match_ignore_list(base_name, global_ignores, iterpool))
363 svn_hash_sets(dirents, base_name, NULL);
369 svn_boolean_t filter = FALSE;
371 SVN_ERR(filter_callback(filter_baton, &filter, local_abspath,
376 svn_hash_sets(dirents, base_name, NULL);
381 svn_pool_destroy(iterpool);
388 import_dir(const svn_delta_editor_t *editor,
390 const char *local_abspath,
391 const char *edit_path,
393 apr_hash_t *excludes,
394 apr_array_header_t *global_ignores,
395 svn_boolean_t no_ignore,
396 svn_boolean_t no_autoprops,
397 svn_boolean_t ignore_unknown_node_types,
398 svn_client_import_filter_func_t filter_callback,
400 import_ctx_t *import_ctx,
401 svn_client_ctx_t *ctx,
405 /* Import the children of DIR_ABSPATH, with other arguments similar to
408 import_children(const char *dir_abspath,
409 const char *edit_path,
411 const svn_delta_editor_t *editor,
414 apr_hash_t *excludes,
415 apr_array_header_t *global_ignores,
416 svn_boolean_t no_ignore,
417 svn_boolean_t no_autoprops,
418 svn_boolean_t ignore_unknown_node_types,
419 svn_client_import_filter_func_t filter_callback,
421 import_ctx_t *import_ctx,
422 svn_client_ctx_t *ctx,
423 apr_pool_t *scratch_pool)
425 apr_array_header_t *sorted_dirents;
427 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
429 sorted_dirents = svn_sort__hash(dirents, svn_sort_compare_items_lexically,
431 for (i = 0; i < sorted_dirents->nelts; i++)
433 const char *this_abspath, *this_edit_path;
434 svn_sort__item_t item = APR_ARRAY_IDX(sorted_dirents, i,
436 const char *filename = item.key;
437 const svn_io_dirent2_t *dirent = item.value;
439 svn_pool_clear(iterpool);
441 if (ctx->cancel_func)
442 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
444 /* Typically, we started importing from ".", in which case
445 edit_path is "". So below, this_path might become "./blah",
446 and this_edit_path might become "blah", for example. */
447 this_abspath = svn_dirent_join(dir_abspath, filename, iterpool);
448 this_edit_path = svn_relpath_join(edit_path, filename, iterpool);
450 if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates)
453 svn_depth_t depth_below_here = depth;
454 if (depth == svn_depth_immediates)
455 depth_below_here = svn_depth_empty;
457 SVN_ERR(import_dir(editor, dir_baton, this_abspath,
458 this_edit_path, depth_below_here, excludes,
459 global_ignores, no_ignore, no_autoprops,
460 ignore_unknown_node_types, filter_callback,
461 filter_baton, import_ctx, ctx, iterpool));
463 else if (dirent->kind == svn_node_file && depth >= svn_depth_files)
465 SVN_ERR(import_file(editor, dir_baton, this_abspath,
466 this_edit_path, dirent,
467 import_ctx, ctx, iterpool));
469 else if (dirent->kind != svn_node_dir && dirent->kind != svn_node_file)
471 if (ignore_unknown_node_types)
474 if (ctx->notify_func2)
476 svn_wc_notify_t *notify
477 = svn_wc_create_notify(this_abspath,
478 svn_wc_notify_skip, iterpool);
479 notify->kind = svn_node_dir;
480 notify->content_state = notify->prop_state
481 = svn_wc_notify_state_inapplicable;
482 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
483 (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
487 return svn_error_createf
488 (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
489 _("Unknown or unversionable type for '%s'"),
490 svn_dirent_local_style(this_abspath, iterpool));
494 svn_pool_destroy(iterpool);
499 /* Import directory LOCAL_ABSPATH into the repository directory indicated by
500 * DIR_BATON in EDITOR. EDIT_PATH is the path imported as the root
501 * directory, so all edits are relative to that.
503 * DEPTH is the depth at this point in the descent (it may be changed
504 * for recursive calls).
506 * Accumulate file paths and their batons in FILES, which must be
507 * non-null. (These are used to send postfix textdeltas later).
509 * EXCLUDES is a hash whose keys are absolute paths to exclude from
510 * the import (values are unused).
512 * GLOBAL_IGNORES is an array of const char * ignore patterns. Any child
513 * of LOCAL_ABSPATH which matches one or more of the patterns is not imported.
515 * If NO_IGNORE is FALSE, don't import files or directories that match
518 * If FILTER_CALLBACK is not NULL, call it with FILTER_BATON on each to be
519 * imported node below LOCAL_ABSPATH to allow filtering nodes.
521 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each
524 * Use POOL for any temporary allocation. */
526 import_dir(const svn_delta_editor_t *editor,
528 const char *local_abspath,
529 const char *edit_path,
531 apr_hash_t *excludes,
532 apr_array_header_t *global_ignores,
533 svn_boolean_t no_ignore,
534 svn_boolean_t no_autoprops,
535 svn_boolean_t ignore_unknown_node_types,
536 svn_client_import_filter_func_t filter_callback,
538 import_ctx_t *import_ctx,
539 svn_client_ctx_t *ctx,
543 void *this_dir_baton;
545 SVN_ERR(svn_path_check_valid(local_abspath, pool));
546 SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes, NULL,
547 global_ignores, filter_callback,
548 filter_baton, ctx, pool, pool));
550 /* Import this directory, but not yet its children. */
552 /* Add the new subdirectory, getting a descent baton from the editor. */
553 SVN_ERR(editor->add_directory(edit_path, dir_baton, NULL,
554 SVN_INVALID_REVNUM, pool, &this_dir_baton));
556 /* Remember that the repository was modified */
557 import_ctx->repos_changed = TRUE;
559 /* By notifying before the recursive call below, we display
560 a directory add before displaying adds underneath the
561 directory. To do it the other way around, just move this
562 after the recursive call. */
563 if (ctx->notify_func2)
565 svn_wc_notify_t *notify
566 = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added,
568 notify->kind = svn_node_dir;
569 notify->content_state = notify->prop_state
570 = svn_wc_notify_state_inapplicable;
571 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
572 (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
576 /* Now import the children recursively. */
577 SVN_ERR(import_children(local_abspath, edit_path, dirents, editor,
578 this_dir_baton, depth, excludes, global_ignores,
579 no_ignore, no_autoprops, ignore_unknown_node_types,
580 filter_callback, filter_baton,
581 import_ctx, ctx, pool));
583 /* Finally, close the sub-directory. */
584 SVN_ERR(editor->close_directory(this_dir_baton, pool));
590 /* Recursively import PATH to a repository using EDITOR and
591 * EDIT_BATON. PATH can be a file or directory.
593 * DEPTH is the depth at which to import PATH; it behaves as for
594 * svn_client_import4().
596 * NEW_ENTRIES is an ordered array of path components that must be
597 * created in the repository (where the ordering direction is
598 * parent-to-child). If PATH is a directory, NEW_ENTRIES may be empty
599 * -- the result is an import which creates as many new entries in the
600 * top repository target directory as there are importable entries in
601 * the top of PATH; but if NEW_ENTRIES is not empty, its last item is
602 * the name of a new subdirectory in the repository to hold the
603 * import. If PATH is a file, NEW_ENTRIES may not be empty, and its
604 * last item is the name used for the file in the repository. If
605 * NEW_ENTRIES contains more than one item, all but the last item are
606 * the names of intermediate directories that are created before the
607 * real import begins. NEW_ENTRIES may NOT be NULL.
609 * EXCLUDES is a hash whose keys are absolute paths to exclude from
610 * the import (values are unused).
612 * AUTOPROPS is hash of all config file autoprops and
613 * svn:auto-props inherited by the import target, see the
614 * IMPORT_CTX member of the same name.
616 * LOCAL_IGNORES is an array of const char * ignore patterns which
617 * correspond to the svn:ignore property (if any) set on the root of the
618 * repository target and thus dictates which immediate children of that
619 * target should be ignored and not imported.
621 * GLOBAL_IGNORES is an array of const char * ignore patterns which
622 * correspond to the svn:global-ignores properties (if any) set on
623 * the root of the repository target or inherited by it.
625 * If NO_IGNORE is FALSE, don't import files or directories that match
628 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for
629 * each imported path, passing actions svn_wc_notify_commit_added.
631 * Use POOL for any temporary allocation.
633 * Note: the repository directory receiving the import was specified
634 * when the editor was fetched. (I.e, when EDITOR->open_root() is
635 * called, it returns a directory baton for that directory, which is
636 * not necessarily the root.)
639 import(const char *local_abspath,
640 const apr_array_header_t *new_entries,
641 const svn_delta_editor_t *editor,
644 apr_hash_t *excludes,
645 apr_hash_t *autoprops,
646 apr_array_header_t *local_ignores,
647 apr_array_header_t *global_ignores,
648 svn_boolean_t no_ignore,
649 svn_boolean_t no_autoprops,
650 svn_boolean_t ignore_unknown_node_types,
651 svn_client_import_filter_func_t filter_callback,
653 svn_client_ctx_t *ctx,
657 apr_array_header_t *batons = NULL;
658 const char *edit_path = "";
659 import_ctx_t *import_ctx = apr_pcalloc(pool, sizeof(*import_ctx));
660 const svn_io_dirent2_t *dirent;
662 import_ctx->autoprops = autoprops;
663 svn_magic__init(&import_ctx->magic_cookie, pool);
665 /* Get a root dir baton. We pass an invalid revnum to open_root
666 to mean "base this on the youngest revision". Should we have an
667 SVN_YOUNGEST_REVNUM defined for these purposes? */
668 SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
671 /* Import a file or a directory tree. */
672 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, FALSE,
675 /* Make the intermediate directory components necessary for properly
676 rooting our import source tree. */
677 if (new_entries->nelts)
681 batons = apr_array_make(pool, new_entries->nelts, sizeof(void *));
682 for (i = 0; i < new_entries->nelts; i++)
684 const char *component = APR_ARRAY_IDX(new_entries, i, const char *);
685 edit_path = svn_relpath_join(edit_path, component, pool);
687 /* If this is the last path component, and we're importing a
688 file, then this component is the name of the file, not an
689 intermediate directory. */
690 if ((i == new_entries->nelts - 1) && (dirent->kind == svn_node_file))
693 APR_ARRAY_PUSH(batons, void *) = root_baton;
694 SVN_ERR(editor->add_directory(edit_path,
696 NULL, SVN_INVALID_REVNUM,
699 /* Remember that the repository was modified */
700 import_ctx->repos_changed = TRUE;
703 else if (dirent->kind == svn_node_file)
705 return svn_error_create
706 (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
707 _("New entry name required when importing a file"));
710 /* Note that there is no need to check whether PATH's basename is
711 the same name that we reserve for our administrative
712 subdirectories. It would be strange -- though not illegal -- to
713 import the contents of a directory of that name, because the
714 directory's own name is not part of those contents. Of course,
715 if something underneath it also has our reserved name, then we'll
718 if (dirent->kind == svn_node_file)
720 /* This code path ignores EXCLUDES and FILTER, but they don't make
721 much sense for a single file import anyway. */
722 svn_boolean_t ignores_match = FALSE;
726 (svn_wc_match_ignore_list(local_abspath, global_ignores, pool)
727 || svn_wc_match_ignore_list(local_abspath, local_ignores, pool));
730 SVN_ERR(import_file(editor, root_baton, local_abspath, edit_path,
731 dirent, import_ctx, ctx, pool));
733 else if (dirent->kind == svn_node_dir)
737 /* If we are creating a new repository directory path to import to,
738 then we disregard any svn:ignore property. */
739 if (!no_ignore && new_entries->nelts)
740 local_ignores = NULL;
742 SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes,
743 local_ignores, global_ignores,
744 filter_callback, filter_baton, ctx,
747 SVN_ERR(import_children(local_abspath, edit_path, dirents, editor,
748 root_baton, depth, excludes, global_ignores,
749 no_ignore, no_autoprops,
750 ignore_unknown_node_types, filter_callback,
751 filter_baton, import_ctx, ctx, pool));
754 else if (dirent->kind == svn_node_none
755 || dirent->kind == svn_node_unknown)
757 return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
758 _("'%s' does not exist"),
759 svn_dirent_local_style(local_abspath, pool));
762 /* Close up shop; it's time to go home. */
763 SVN_ERR(editor->close_directory(root_baton, pool));
764 if (batons && batons->nelts)
767 while ((baton = (void **) apr_array_pop(batons)))
769 SVN_ERR(editor->close_directory(*baton, pool));
773 if (import_ctx->repos_changed)
774 return editor->close_edit(edit_baton, pool);
776 return editor->abort_edit(edit_baton, pool);
780 /*** Public Interfaces. ***/
783 svn_client_import5(const char *path,
786 svn_boolean_t no_ignore,
787 svn_boolean_t no_autoprops,
788 svn_boolean_t ignore_unknown_node_types,
789 const apr_hash_t *revprop_table,
790 svn_client_import_filter_func_t filter_callback,
792 svn_commit_callback2_t commit_callback,
794 svn_client_ctx_t *ctx,
795 apr_pool_t *scratch_pool)
797 svn_error_t *err = SVN_NO_ERROR;
798 const char *log_msg = "";
799 const svn_delta_editor_t *editor;
801 svn_ra_session_t *ra_session;
802 apr_hash_t *excludes = apr_hash_make(scratch_pool);
803 svn_node_kind_t kind;
804 const char *local_abspath;
805 apr_array_header_t *new_entries = apr_array_make(scratch_pool, 4,
806 sizeof(const char *));
807 apr_hash_t *commit_revprops;
808 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
809 apr_hash_t *autoprops = NULL;
810 apr_array_header_t *global_ignores;
811 apr_array_header_t *local_ignores_arr;
813 if (svn_path_is_url(path))
814 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
815 _("'%s' is not a local path"), path);
817 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
819 /* Create a new commit item and add it to the array. */
820 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
822 /* If there's a log message gatherer, create a temporary commit
823 item array solely to help generate the log message. The
824 array is not used for the import itself. */
825 svn_client_commit_item3_t *item;
826 const char *tmp_file;
827 apr_array_header_t *commit_items
828 = apr_array_make(scratch_pool, 1, sizeof(item));
830 item = svn_client_commit_item3_create(scratch_pool);
831 item->path = apr_pstrdup(scratch_pool, path);
832 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
833 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
835 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
841 const char *abs_path;
842 SVN_ERR(svn_dirent_get_absolute(&abs_path, tmp_file, scratch_pool));
843 svn_hash_sets(excludes, abs_path, (void *)1);
847 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
849 SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
850 ctx, scratch_pool, iterpool));
852 /* Figure out all the path components we need to create just to have
853 a place to stick our imported tree. */
854 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
857 /* We can import into directories, but if a file already exists, that's
859 if (kind == svn_node_file)
860 return svn_error_createf
861 (SVN_ERR_ENTRY_EXISTS, NULL,
862 _("Path '%s' already exists"), url);
864 while (kind == svn_node_none)
868 svn_pool_clear(iterpool);
870 svn_uri_split(&url, &dir, url, scratch_pool);
871 APR_ARRAY_PUSH(new_entries, const char *) = dir;
872 SVN_ERR(svn_ra_reparent(ra_session, url, iterpool));
874 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
878 /* Reverse the order of the components we added to our NEW_ENTRIES array. */
879 svn_sort__array_reverse(new_entries, scratch_pool);
881 /* The repository doesn't know about the reserved administrative
883 if (new_entries->nelts)
885 const char *last_component
886 = APR_ARRAY_IDX(new_entries, new_entries->nelts - 1, const char *);
888 if (svn_wc_is_adm_dir(last_component, scratch_pool))
889 return svn_error_createf
890 (SVN_ERR_CL_ADM_DIR_RESERVED, NULL,
891 _("'%s' is a reserved name and cannot be imported"),
892 svn_dirent_local_style(last_component, scratch_pool));
895 SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
896 log_msg, ctx, scratch_pool));
898 /* Fetch RA commit editor. */
899 SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
900 svn_client__get_shim_callbacks(ctx->wc_ctx,
901 NULL, scratch_pool)));
902 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
903 commit_revprops, commit_callback,
904 commit_baton, NULL, TRUE,
907 /* Get inherited svn:auto-props, svn:global-ignores, and
908 svn:ignores for the location we are importing to. */
910 SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx,
911 scratch_pool, iterpool));
914 global_ignores = NULL;
915 local_ignores_arr = NULL;
919 svn_opt_revision_t rev;
920 apr_array_header_t *config_ignores;
921 apr_hash_t *local_ignores_hash;
923 SVN_ERR(svn_client__get_inherited_ignores(&global_ignores, url, ctx,
924 scratch_pool, iterpool));
925 SVN_ERR(svn_wc_get_default_ignores(&config_ignores, ctx->config,
927 global_ignores = apr_array_append(scratch_pool, global_ignores,
930 rev.kind = svn_opt_revision_head;
931 SVN_ERR(svn_client_propget5(&local_ignores_hash, NULL, SVN_PROP_IGNORE, url,
932 &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
933 scratch_pool, scratch_pool));
934 local_ignores_arr = apr_array_make(scratch_pool, 1, sizeof(const char *));
936 if (apr_hash_count(local_ignores_hash))
938 svn_string_t *propval = svn_hash_gets(local_ignores_hash, url);
941 svn_cstring_split_append(local_ignores_arr, propval->data,
942 "\n\r\t\v ", FALSE, scratch_pool);
947 /* If an error occurred during the commit, abort the edit and return
948 the error. We don't even care if the abort itself fails. */
949 if ((err = import(local_abspath, new_entries, editor, edit_baton,
950 depth, excludes, autoprops, local_ignores_arr,
951 global_ignores, no_ignore, no_autoprops,
952 ignore_unknown_node_types, filter_callback,
953 filter_baton, ctx, iterpool)))
955 return svn_error_compose_create(
957 editor->abort_edit(edit_baton, iterpool));
960 svn_pool_destroy(iterpool);