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_ra_private.h"
51 #include "private/svn_sorts_private.h"
52 #include "private/svn_subr_private.h"
53 #include "private/svn_magic.h"
55 #include "svn_private_config.h"
57 /* Import context baton. */
59 typedef struct import_ctx_t
61 /* Whether any changes were made to the repository */
62 svn_boolean_t repos_changed;
64 /* A magic cookie for mime-type detection. */
65 svn_magic__cookie_t *magic_cookie;
67 /* Collection of all possible configuration file dictated auto-props and
68 svn:auto-props. A hash mapping const char * file patterns to a
69 second hash which maps const char * property names to const char *
70 property values. Properties which don't have a value, e.g.
71 svn:executable, simply map the property name to an empty string.
72 May be NULL if autoprops are disabled. */
73 apr_hash_t *autoprops;
77 /* Apply LOCAL_ABSPATH's contents (as a delta against the empty string) to
78 FILE_BATON in EDITOR. Use POOL for any temporary allocation.
79 PROPERTIES is the set of node properties set on this file.
81 Fill DIGEST with the md5 checksum of the sent file; DIGEST must be
82 at least APR_MD5_DIGESTSIZE bytes long. */
84 /* ### how does this compare against svn_wc_transmit_text_deltas2() ??? */
87 send_file_contents(const char *local_abspath,
89 const svn_delta_editor_t *editor,
90 apr_hash_t *properties,
91 unsigned char *digest,
94 svn_stream_t *contents;
95 svn_txdelta_window_handler_t handler;
97 const svn_string_t *eol_style_val = NULL, *keywords_val = NULL;
98 svn_boolean_t special = FALSE;
99 svn_subst_eol_style_t eol_style;
101 apr_hash_t *keywords;
103 /* If there are properties, look for EOL-style and keywords ones. */
106 eol_style_val = apr_hash_get(properties, SVN_PROP_EOL_STYLE,
107 sizeof(SVN_PROP_EOL_STYLE) - 1);
108 keywords_val = apr_hash_get(properties, SVN_PROP_KEYWORDS,
109 sizeof(SVN_PROP_KEYWORDS) - 1);
110 if (svn_hash_gets(properties, SVN_PROP_SPECIAL))
114 /* Get an editor func that wants to consume the delta stream. */
115 SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool,
116 &handler, &handler_baton));
119 svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data);
123 eol_style = svn_subst_eol_style_none;
127 SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data,
128 APR_STRINGIFY(SVN_INVALID_REVNUM),
129 "", "", 0, "", pool));
135 SVN_ERR(svn_subst_read_specialfile(&contents, local_abspath,
140 /* Open the working copy file. */
141 SVN_ERR(svn_stream_open_readonly(&contents, local_abspath, pool, pool));
143 /* If we have EOL styles or keywords, then detranslate the file. */
144 if (svn_subst_translation_required(eol_style, eol, keywords,
147 if (eol_style == svn_subst_eol_style_unknown)
148 return svn_error_createf(SVN_ERR_IO_UNKNOWN_EOL, NULL,
149 _("%s property on '%s' contains "
150 "unrecognized EOL-style '%s'"),
152 svn_dirent_local_style(local_abspath,
154 eol_style_val->data);
156 /* We're importing, so translate files with 'native' eol-style to
157 * repository-normal form, not to this platform's native EOL. */
158 if (eol_style == svn_subst_eol_style_native)
159 eol = SVN_SUBST_NATIVE_EOL_STR;
161 /* Wrap the working copy stream with a filter to detranslate it. */
162 contents = svn_subst_stream_translated(contents,
171 /* Send the file's contents to the delta-window handler. */
172 return svn_error_trace(svn_txdelta_send_stream(contents, handler,
173 handler_baton, digest,
178 /* Import file PATH as EDIT_PATH in the repository directory indicated
179 * by DIR_BATON in EDITOR.
181 * Accumulate file paths and their batons in FILES, which must be
182 * non-null. (These are used to send postfix textdeltas later).
184 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON
187 * Use POOL for any temporary allocation.
190 import_file(const svn_delta_editor_t *editor,
192 const char *local_abspath,
193 const char *edit_path,
194 const svn_io_dirent2_t *dirent,
195 import_ctx_t *import_ctx,
196 svn_client_ctx_t *ctx,
200 const char *mimetype = NULL;
201 unsigned char digest[APR_MD5_DIGESTSIZE];
202 const char *text_checksum;
203 apr_hash_t* properties;
204 apr_hash_index_t *hi;
206 SVN_ERR(svn_path_check_valid(local_abspath, pool));
208 /* Add the file, using the pool from the FILES hash. */
209 SVN_ERR(editor->add_file(edit_path, dir_baton, NULL, SVN_INVALID_REVNUM,
212 /* Remember that the repository was modified */
213 import_ctx->repos_changed = TRUE;
215 if (! dirent->special)
217 /* add automatic properties */
218 SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype,
220 import_ctx->magic_cookie,
221 import_ctx->autoprops,
225 properties = apr_hash_make(pool);
229 for (hi = apr_hash_first(pool, properties); hi; hi = apr_hash_next(hi))
231 const char *pname = apr_hash_this_key(hi);
232 const svn_string_t *pval = apr_hash_this_val(hi);
234 SVN_ERR(editor->change_file_prop(file_baton, pname, pval, pool));
238 if (ctx->notify_func2)
240 svn_wc_notify_t *notify
241 = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added,
243 notify->kind = svn_node_file;
244 notify->mime_type = mimetype;
245 notify->content_state = notify->prop_state
246 = svn_wc_notify_state_inapplicable;
247 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
248 ctx->notify_func2(ctx->notify_baton2, notify, pool);
251 /* If this is a special file, we need to set the svn:special
252 property and create a temporary detranslated version in order to
253 send to the server. */
256 svn_hash_sets(properties, SVN_PROP_SPECIAL,
257 svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool));
258 SVN_ERR(editor->change_file_prop(file_baton, SVN_PROP_SPECIAL,
259 svn_hash_gets(properties,
264 /* Now, transmit the file contents. */
265 SVN_ERR(send_file_contents(local_abspath, file_baton, editor,
266 properties, digest, pool));
268 /* Finally, close the file. */
270 svn_checksum_to_cstring(svn_checksum__from_digest_md5(digest, pool), pool);
272 return svn_error_trace(editor->close_file(file_baton, text_checksum, pool));
276 /* Return in CHILDREN a mapping of basenames to dirents for the importable
277 * children of DIR_ABSPATH. EXCLUDES is a hash of absolute paths to filter
278 * out. IGNORES and GLOBAL_IGNORES, if non-NULL, are lists of basename
279 * patterns to filter out.
280 * FILTER_CALLBACK and FILTER_BATON will be called for each absolute path,
281 * allowing users to further filter the list of returned entries.
283 * Results are returned in RESULT_POOL; use SCRATCH_POOL for temporary data.*/
285 get_filtered_children(apr_hash_t **children,
286 const char *dir_abspath,
287 apr_hash_t *excludes,
288 apr_array_header_t *ignores,
289 apr_array_header_t *global_ignores,
290 svn_client_import_filter_func_t filter_callback,
292 svn_client_ctx_t *ctx,
293 apr_pool_t *result_pool,
294 apr_pool_t *scratch_pool)
297 apr_hash_index_t *hi;
298 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
300 SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, result_pool,
303 for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
305 const char *base_name = apr_hash_this_key(hi);
306 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
307 const char *local_abspath;
309 svn_pool_clear(iterpool);
311 local_abspath = svn_dirent_join(dir_abspath, base_name, iterpool);
313 if (svn_wc_is_adm_dir(base_name, iterpool))
315 /* If someone's trying to import a directory named the same
316 as our administrative directories, that's probably not
317 what they wanted to do. If they are importing a file
318 with that name, something is bound to blow up when they
319 checkout what they've imported. So, just skip items with
321 if (ctx->notify_func2)
323 svn_wc_notify_t *notify
324 = svn_wc_create_notify(svn_dirent_join(local_abspath, base_name,
326 svn_wc_notify_skip, iterpool);
327 notify->kind = svn_node_dir;
328 notify->content_state = notify->prop_state
329 = svn_wc_notify_state_inapplicable;
330 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
331 ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
334 svn_hash_sets(dirents, base_name, NULL);
337 /* If this is an excluded path, exclude it. */
338 if (svn_hash_gets(excludes, local_abspath))
340 svn_hash_sets(dirents, base_name, NULL);
344 if (ignores && svn_wc_match_ignore_list(base_name, ignores, iterpool))
346 svn_hash_sets(dirents, base_name, NULL);
350 if (global_ignores &&
351 svn_wc_match_ignore_list(base_name, global_ignores, iterpool))
353 svn_hash_sets(dirents, base_name, NULL);
359 svn_boolean_t filter = FALSE;
361 SVN_ERR(filter_callback(filter_baton, &filter, local_abspath,
366 svn_hash_sets(dirents, base_name, NULL);
371 svn_pool_destroy(iterpool);
378 import_dir(const svn_delta_editor_t *editor,
380 const char *local_abspath,
381 const char *edit_path,
383 apr_hash_t *excludes,
384 apr_array_header_t *global_ignores,
385 svn_boolean_t no_ignore,
386 svn_boolean_t no_autoprops,
387 svn_boolean_t ignore_unknown_node_types,
388 svn_client_import_filter_func_t filter_callback,
390 import_ctx_t *import_ctx,
391 svn_client_ctx_t *ctx,
395 /* Import the children of DIR_ABSPATH, with other arguments similar to
398 import_children(const char *dir_abspath,
399 const char *edit_path,
401 const svn_delta_editor_t *editor,
404 apr_hash_t *excludes,
405 apr_array_header_t *global_ignores,
406 svn_boolean_t no_ignore,
407 svn_boolean_t no_autoprops,
408 svn_boolean_t ignore_unknown_node_types,
409 svn_client_import_filter_func_t filter_callback,
411 import_ctx_t *import_ctx,
412 svn_client_ctx_t *ctx,
413 apr_pool_t *scratch_pool)
415 apr_array_header_t *sorted_dirents;
417 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
419 sorted_dirents = svn_sort__hash(dirents, svn_sort_compare_items_lexically,
421 for (i = 0; i < sorted_dirents->nelts; i++)
423 const char *this_abspath, *this_edit_path;
424 svn_sort__item_t item = APR_ARRAY_IDX(sorted_dirents, i,
426 const char *filename = item.key;
427 const svn_io_dirent2_t *dirent = item.value;
429 svn_pool_clear(iterpool);
431 if (ctx->cancel_func)
432 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
434 /* Typically, we started importing from ".", in which case
435 edit_path is "". So below, this_path might become "./blah",
436 and this_edit_path might become "blah", for example. */
437 this_abspath = svn_dirent_join(dir_abspath, filename, iterpool);
438 this_edit_path = svn_relpath_join(edit_path, filename, iterpool);
440 if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates)
443 svn_depth_t depth_below_here = depth;
444 if (depth == svn_depth_immediates)
445 depth_below_here = svn_depth_empty;
447 SVN_ERR(import_dir(editor, dir_baton, this_abspath,
448 this_edit_path, depth_below_here, excludes,
449 global_ignores, no_ignore, no_autoprops,
450 ignore_unknown_node_types, filter_callback,
451 filter_baton, import_ctx, ctx, iterpool));
453 else if (dirent->kind == svn_node_file && depth >= svn_depth_files)
455 SVN_ERR(import_file(editor, dir_baton, this_abspath,
456 this_edit_path, dirent,
457 import_ctx, ctx, iterpool));
459 else if (dirent->kind != svn_node_dir && dirent->kind != svn_node_file)
461 if (ignore_unknown_node_types)
464 if (ctx->notify_func2)
466 svn_wc_notify_t *notify
467 = svn_wc_create_notify(this_abspath,
468 svn_wc_notify_skip, iterpool);
469 notify->kind = svn_node_dir;
470 notify->content_state = notify->prop_state
471 = svn_wc_notify_state_inapplicable;
472 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
473 ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
477 return svn_error_createf
478 (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
479 _("Unknown or unversionable type for '%s'"),
480 svn_dirent_local_style(this_abspath, iterpool));
484 svn_pool_destroy(iterpool);
489 /* Import directory LOCAL_ABSPATH into the repository directory indicated by
490 * DIR_BATON in EDITOR. EDIT_PATH is the path imported as the root
491 * directory, so all edits are relative to that.
493 * DEPTH is the depth at this point in the descent (it may be changed
494 * for recursive calls).
496 * Accumulate file paths and their batons in FILES, which must be
497 * non-null. (These are used to send postfix textdeltas later).
499 * EXCLUDES is a hash whose keys are absolute paths to exclude from
500 * the import (values are unused).
502 * GLOBAL_IGNORES is an array of const char * ignore patterns. Any child
503 * of LOCAL_ABSPATH which matches one or more of the patterns is not imported.
505 * If NO_IGNORE is FALSE, don't import files or directories that match
508 * If FILTER_CALLBACK is not NULL, call it with FILTER_BATON on each to be
509 * imported node below LOCAL_ABSPATH to allow filtering nodes.
511 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each
514 * Use POOL for any temporary allocation. */
516 import_dir(const svn_delta_editor_t *editor,
518 const char *local_abspath,
519 const char *edit_path,
521 apr_hash_t *excludes,
522 apr_array_header_t *global_ignores,
523 svn_boolean_t no_ignore,
524 svn_boolean_t no_autoprops,
525 svn_boolean_t ignore_unknown_node_types,
526 svn_client_import_filter_func_t filter_callback,
528 import_ctx_t *import_ctx,
529 svn_client_ctx_t *ctx,
533 void *this_dir_baton;
535 SVN_ERR(svn_path_check_valid(local_abspath, pool));
536 SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes, NULL,
537 global_ignores, filter_callback,
538 filter_baton, ctx, pool, pool));
540 /* Import this directory, but not yet its children. */
542 /* Add the new subdirectory, getting a descent baton from the editor. */
543 SVN_ERR(editor->add_directory(edit_path, dir_baton, NULL,
544 SVN_INVALID_REVNUM, pool, &this_dir_baton));
546 /* Remember that the repository was modified */
547 import_ctx->repos_changed = TRUE;
549 /* By notifying before the recursive call below, we display
550 a directory add before displaying adds underneath the
551 directory. To do it the other way around, just move this
552 after the recursive call. */
553 if (ctx->notify_func2)
555 svn_wc_notify_t *notify
556 = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added,
558 notify->kind = svn_node_dir;
559 notify->content_state = notify->prop_state
560 = svn_wc_notify_state_inapplicable;
561 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
562 ctx->notify_func2(ctx->notify_baton2, notify, pool);
566 /* Now import the children recursively. */
567 SVN_ERR(import_children(local_abspath, edit_path, dirents, editor,
568 this_dir_baton, depth, excludes, global_ignores,
569 no_ignore, no_autoprops, ignore_unknown_node_types,
570 filter_callback, filter_baton,
571 import_ctx, ctx, pool));
573 /* Finally, close the sub-directory. */
574 SVN_ERR(editor->close_directory(this_dir_baton, pool));
580 /* Recursively import PATH to a repository using EDITOR and
581 * EDIT_BATON. PATH can be a file or directory.
583 * Sets *UPDATED_REPOSITORY to TRUE when the repository was modified by
584 * a successfull commit, otherwise to FALSE.
586 * DEPTH is the depth at which to import PATH; it behaves as for
587 * svn_client_import4().
589 * BASE_REV is the revision to use for the root of the commit. We
590 * checked the preconditions against this revision.
592 * NEW_ENTRIES is an ordered array of path components that must be
593 * created in the repository (where the ordering direction is
594 * parent-to-child). If PATH is a directory, NEW_ENTRIES may be empty
595 * -- the result is an import which creates as many new entries in the
596 * top repository target directory as there are importable entries in
597 * the top of PATH; but if NEW_ENTRIES is not empty, its last item is
598 * the name of a new subdirectory in the repository to hold the
599 * import. If PATH is a file, NEW_ENTRIES may not be empty, and its
600 * last item is the name used for the file in the repository. If
601 * NEW_ENTRIES contains more than one item, all but the last item are
602 * the names of intermediate directories that are created before the
603 * real import begins. NEW_ENTRIES may NOT be NULL.
605 * EXCLUDES is a hash whose keys are absolute paths to exclude from
606 * the import (values are unused).
608 * AUTOPROPS is hash of all config file autoprops and
609 * svn:auto-props inherited by the import target, see the
610 * IMPORT_CTX member of the same name.
612 * LOCAL_IGNORES is an array of const char * ignore patterns which
613 * correspond to the svn:ignore property (if any) set on the root of the
614 * repository target and thus dictates which immediate children of that
615 * target should be ignored and not imported.
617 * GLOBAL_IGNORES is an array of const char * ignore patterns which
618 * correspond to the svn:global-ignores properties (if any) set on
619 * the root of the repository target or inherited by it.
621 * If NO_IGNORE is FALSE, don't import files or directories that match
624 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for
625 * each imported path, passing actions svn_wc_notify_commit_added.
627 * Use POOL for any temporary allocation.
629 * Note: the repository directory receiving the import was specified
630 * when the editor was fetched. (I.e, when EDITOR->open_root() is
631 * called, it returns a directory baton for that directory, which is
632 * not necessarily the root.)
635 import(svn_boolean_t *updated_repository,
636 const char *local_abspath,
638 const apr_array_header_t *new_entries,
639 const svn_delta_editor_t *editor,
642 svn_revnum_t base_rev,
643 apr_hash_t *excludes,
644 apr_hash_t *autoprops,
645 apr_array_header_t *local_ignores,
646 apr_array_header_t *global_ignores,
647 svn_boolean_t no_ignore,
648 svn_boolean_t no_autoprops,
649 svn_boolean_t ignore_unknown_node_types,
650 svn_client_import_filter_func_t filter_callback,
652 svn_client_ctx_t *ctx,
656 apr_array_header_t *batons = NULL;
657 const char *edit_path = "";
658 import_ctx_t import_ctx = { FALSE };
659 const svn_io_dirent2_t *dirent;
661 *updated_repository = FALSE;
663 import_ctx.autoprops = autoprops;
664 SVN_ERR(svn_magic__init(&import_ctx.magic_cookie, ctx->config, pool));
666 /* Get a root dir baton. We pass the revnum we used for testing our
667 assumptions and obtaining inherited properties. */
668 SVN_ERR(editor->open_root(edit_baton, base_rev, pool, &root_baton));
670 /* Import a file or a directory tree. */
671 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, FALSE,
674 /* Make the intermediate directory components necessary for properly
675 rooting our import source tree. */
676 if (new_entries->nelts)
680 batons = apr_array_make(pool, new_entries->nelts, sizeof(void *));
681 for (i = 0; i < new_entries->nelts; i++)
683 const char *component = APR_ARRAY_IDX(new_entries, i, const char *);
684 edit_path = svn_relpath_join(edit_path, component, pool);
686 /* If this is the last path component, and we're importing a
687 file, then this component is the name of the file, not an
688 intermediate directory. */
689 if ((i == new_entries->nelts - 1) && (dirent->kind == svn_node_file))
692 APR_ARRAY_PUSH(batons, void *) = root_baton;
693 SVN_ERR(editor->add_directory(edit_path,
695 NULL, SVN_INVALID_REVNUM,
698 /* Remember that the repository was modified */
699 import_ctx.repos_changed = TRUE;
702 else if (dirent->kind == svn_node_file)
704 return svn_error_create
705 (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
706 _("New entry name required when importing a file"));
709 /* Note that there is no need to check whether PATH's basename is
710 the same name that we reserve for our administrative
711 subdirectories. It would be strange -- though not illegal -- to
712 import the contents of a directory of that name, because the
713 directory's own name is not part of those contents. Of course,
714 if something underneath it also has our reserved name, then we'll
717 if (dirent->kind == svn_node_file)
719 /* This code path ignores EXCLUDES and FILTER, but they don't make
720 much sense for a single file import anyway. */
721 svn_boolean_t ignores_match = FALSE;
725 (svn_wc_match_ignore_list(local_abspath, global_ignores, pool)
726 || svn_wc_match_ignore_list(local_abspath, local_ignores, pool));
729 SVN_ERR(import_file(editor, root_baton, local_abspath, edit_path,
730 dirent, &import_ctx, ctx, pool));
732 else if (dirent->kind == svn_node_dir)
736 /* If we are creating a new repository directory path to import to,
737 then we disregard any svn:ignore property. */
738 if (!no_ignore && new_entries->nelts)
739 local_ignores = NULL;
741 SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes,
742 local_ignores, global_ignores,
743 filter_callback, filter_baton, ctx,
746 SVN_ERR(import_children(local_abspath, edit_path, dirents, editor,
747 root_baton, depth, excludes, global_ignores,
748 no_ignore, no_autoprops,
749 ignore_unknown_node_types, filter_callback,
750 filter_baton, &import_ctx, ctx, pool));
753 else if (dirent->kind == svn_node_none
754 || dirent->kind == svn_node_unknown)
756 return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
757 _("'%s' does not exist"),
758 svn_dirent_local_style(local_abspath, pool));
761 /* Close up shop; it's time to go home. */
762 SVN_ERR(editor->close_directory(root_baton, pool));
763 if (batons && batons->nelts)
766 while ((baton = (void **) apr_array_pop(batons)))
768 SVN_ERR(editor->close_directory(*baton, pool));
772 if (import_ctx.repos_changed)
774 if (ctx->notify_func2)
776 svn_wc_notify_t *notify;
777 notify = svn_wc_create_notify_url(url,
778 svn_wc_notify_commit_finalizing,
780 ctx->notify_func2(ctx->notify_baton2, notify, pool);
783 SVN_ERR(editor->close_edit(edit_baton, pool));
785 *updated_repository = TRUE;
792 /*** Public Interfaces. ***/
795 svn_client_import5(const char *path,
798 svn_boolean_t no_ignore,
799 svn_boolean_t no_autoprops,
800 svn_boolean_t ignore_unknown_node_types,
801 const apr_hash_t *revprop_table,
802 svn_client_import_filter_func_t filter_callback,
804 svn_commit_callback2_t commit_callback,
806 svn_client_ctx_t *ctx,
807 apr_pool_t *scratch_pool)
809 svn_error_t *err = SVN_NO_ERROR;
810 const char *log_msg = "";
811 const svn_delta_editor_t *editor;
813 svn_ra_session_t *ra_session;
814 apr_hash_t *excludes = apr_hash_make(scratch_pool);
815 svn_node_kind_t kind;
816 const char *local_abspath;
817 apr_array_header_t *new_entries = apr_array_make(scratch_pool, 4,
818 sizeof(const char *));
819 apr_hash_t *commit_revprops;
820 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
821 apr_hash_t *autoprops = NULL;
822 apr_array_header_t *global_ignores;
823 apr_array_header_t *local_ignores_arr;
824 svn_revnum_t base_rev;
825 apr_array_header_t *inherited_props = NULL;
826 apr_hash_t *url_props = NULL;
827 svn_boolean_t updated_repository;
829 if (svn_path_is_url(path))
830 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
831 _("'%s' is not a local path"), path);
833 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
835 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
837 /* Create a new commit item and add it to the array. */
838 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
840 /* If there's a log message gatherer, create a temporary commit
841 item array solely to help generate the log message. The
842 array is not used for the import itself. */
843 svn_client_commit_item3_t *item;
844 const char *tmp_file;
845 apr_array_header_t *commit_items
846 = apr_array_make(scratch_pool, 1, sizeof(item));
848 item = svn_client_commit_item3_create(scratch_pool);
849 item->path = local_abspath;
852 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
853 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
855 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
861 const char *abs_path;
862 SVN_ERR(svn_dirent_get_absolute(&abs_path, tmp_file, scratch_pool));
863 svn_hash_sets(excludes, abs_path, (void *)1);
867 SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
868 ctx, scratch_pool, iterpool));
870 SVN_ERR(svn_ra_get_latest_revnum(ra_session, &base_rev, iterpool));
872 /* Figure out all the path components we need to create just to have
873 a place to stick our imported tree. */
874 SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool));
876 /* We can import into directories, but if a file already exists, that's
878 if (kind == svn_node_file)
879 return svn_error_createf
880 (SVN_ERR_ENTRY_EXISTS, NULL,
881 _("Path '%s' already exists"), url);
883 while (kind == svn_node_none)
887 svn_pool_clear(iterpool);
889 svn_uri_split(&url, &dir, url, scratch_pool);
890 APR_ARRAY_PUSH(new_entries, const char *) = dir;
891 SVN_ERR(svn_ra_reparent(ra_session, url, iterpool));
893 SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool));
896 /* Reverse the order of the components we added to our NEW_ENTRIES array. */
897 svn_sort__array_reverse(new_entries, scratch_pool);
899 /* The repository doesn't know about the reserved administrative
901 if (new_entries->nelts)
903 const char *last_component
904 = APR_ARRAY_IDX(new_entries, new_entries->nelts - 1, const char *);
906 if (svn_wc_is_adm_dir(last_component, scratch_pool))
907 return svn_error_createf
908 (SVN_ERR_CL_ADM_DIR_RESERVED, NULL,
909 _("'%s' is a reserved name and cannot be imported"),
910 svn_dirent_local_style(last_component, scratch_pool));
913 SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
914 log_msg, ctx, scratch_pool));
916 /* Obtain properties before opening the commit editor, as at that point we are
917 not allowed to use the existing ra-session */
918 if (! no_ignore /*|| ! no_autoprops*/)
920 SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &url_props, "",
921 base_rev, SVN_DIRENT_KIND, scratch_pool));
923 SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", base_rev,
924 scratch_pool, iterpool));
927 /* Fetch RA commit editor. */
928 SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
929 svn_client__get_shim_callbacks(ctx->wc_ctx,
930 NULL, scratch_pool)));
931 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
932 commit_revprops, commit_callback,
933 commit_baton, NULL, TRUE,
936 /* Get inherited svn:auto-props, svn:global-ignores, and
937 svn:ignores for the location we are importing to. */
940 /* ### This should use inherited_props and url_props to avoid creating
941 another ra session to obtain the same values, but using a possibly
942 different HEAD revision */
943 SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx,
944 scratch_pool, iterpool));
948 global_ignores = NULL;
949 local_ignores_arr = NULL;
953 apr_array_header_t *config_ignores;
957 global_ignores = apr_array_make(scratch_pool, 64, sizeof(const char *));
959 SVN_ERR(svn_wc_get_default_ignores(&config_ignores, ctx->config,
961 global_ignores = apr_array_append(scratch_pool, global_ignores,
964 val = svn_hash_gets(url_props, SVN_PROP_INHERITABLE_IGNORES);
966 svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ",
967 FALSE, scratch_pool);
969 for (i = 0; i < inherited_props->nelts; i++)
971 svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
972 inherited_props, i, svn_prop_inherited_item_t *);
974 val = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES);
977 svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ",
978 FALSE, scratch_pool);
980 local_ignores_arr = apr_array_make(scratch_pool, 1, sizeof(const char *));
982 val = svn_hash_gets(url_props, SVN_PROP_IGNORE);
986 svn_cstring_split_append(local_ignores_arr, val->data,
987 "\n\r\t\v ", FALSE, scratch_pool);
991 /* If an error occurred during the commit, properly abort the edit. */
992 err = svn_error_trace(import(&updated_repository,
993 local_abspath, url, new_entries, editor,
994 edit_baton, depth, base_rev, excludes,
995 autoprops, local_ignores_arr, global_ignores,
996 no_ignore, no_autoprops,
997 ignore_unknown_node_types, filter_callback,
998 filter_baton, ctx, iterpool));
1000 svn_pool_destroy(iterpool);
1002 if (err || !updated_repository)
1004 return svn_error_compose_create(
1006 editor->abort_edit(edit_baton, scratch_pool));
1009 return SVN_NO_ERROR;