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;
76 typedef struct open_txdelta_stream_baton_t
78 svn_boolean_t need_reset;
80 } open_txdelta_stream_baton_t;
82 /* Implements svn_txdelta_stream_open_func_t */
84 open_txdelta_stream(svn_txdelta_stream_t **txdelta_stream_p,
86 apr_pool_t *result_pool,
87 apr_pool_t *scratch_pool)
89 open_txdelta_stream_baton_t *b = baton;
93 /* Under rare circumstances, we can be restarted and would need to
94 * supply the delta stream again. In this case, reset the base
96 SVN_ERR(svn_stream_reset(b->stream));
99 /* Get the delta stream (delta against the empty string). */
100 svn_txdelta2(txdelta_stream_p, svn_stream_empty(result_pool),
101 b->stream, FALSE, result_pool);
102 b->need_reset = TRUE;
106 /* Apply LOCAL_ABSPATH's contents (as a delta against the empty string) to
107 FILE_BATON in EDITOR. Use POOL for any temporary allocation.
108 PROPERTIES is the set of node properties set on this file.
110 Return the resulting checksum in *RESULT_MD5_CHECKSUM_P. */
112 /* ### how does this compare against svn_wc_transmit_text_deltas2() ??? */
115 send_file_contents(svn_checksum_t **result_md5_checksum_p,
116 const char *local_abspath,
118 const svn_delta_editor_t *editor,
119 apr_hash_t *properties,
122 svn_stream_t *contents;
123 const svn_string_t *eol_style_val = NULL, *keywords_val = NULL;
124 svn_boolean_t special = FALSE;
125 svn_subst_eol_style_t eol_style;
127 apr_hash_t *keywords;
128 open_txdelta_stream_baton_t baton = { 0 };
130 /* If there are properties, look for EOL-style and keywords ones. */
133 eol_style_val = apr_hash_get(properties, SVN_PROP_EOL_STYLE,
134 sizeof(SVN_PROP_EOL_STYLE) - 1);
135 keywords_val = apr_hash_get(properties, SVN_PROP_KEYWORDS,
136 sizeof(SVN_PROP_KEYWORDS) - 1);
137 if (svn_hash_gets(properties, SVN_PROP_SPECIAL))
142 svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data);
146 eol_style = svn_subst_eol_style_none;
150 SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data,
151 APR_STRINGIFY(SVN_INVALID_REVNUM),
152 "", "", 0, "", pool));
158 SVN_ERR(svn_subst_read_specialfile(&contents, local_abspath,
163 /* Open the working copy file. */
164 SVN_ERR(svn_stream_open_readonly(&contents, local_abspath, pool, pool));
166 /* If we have EOL styles or keywords, then detranslate the file. */
167 if (svn_subst_translation_required(eol_style, eol, keywords,
170 if (eol_style == svn_subst_eol_style_unknown)
171 return svn_error_createf(SVN_ERR_IO_UNKNOWN_EOL, NULL,
172 _("%s property on '%s' contains "
173 "unrecognized EOL-style '%s'"),
175 svn_dirent_local_style(local_abspath,
177 eol_style_val->data);
179 /* We're importing, so translate files with 'native' eol-style to
180 * repository-normal form, not to this platform's native EOL. */
181 if (eol_style == svn_subst_eol_style_native)
182 eol = SVN_SUBST_NATIVE_EOL_STR;
184 /* Wrap the working copy stream with a filter to detranslate it. */
185 contents = svn_subst_stream_translated(contents,
194 /* Arrange the stream to calculate the resulting MD5. */
195 contents = svn_stream_checksummed2(contents, result_md5_checksum_p, NULL,
196 svn_checksum_md5, TRUE, pool);
197 /* Send the contents. */
198 baton.need_reset = FALSE;
199 baton.stream = svn_stream_disown(contents, pool);
200 SVN_ERR(editor->apply_textdelta_stream(editor, file_baton, NULL,
201 open_txdelta_stream, &baton, pool));
202 SVN_ERR(svn_stream_close(contents));
208 /* Import file PATH as EDIT_PATH in the repository directory indicated
209 * by DIR_BATON in EDITOR.
211 * Accumulate file paths and their batons in FILES, which must be
212 * non-null. (These are used to send postfix textdeltas later).
214 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON
217 * Use POOL for any temporary allocation.
220 import_file(const svn_delta_editor_t *editor,
222 const char *local_abspath,
223 const char *edit_path,
224 const svn_io_dirent2_t *dirent,
225 import_ctx_t *import_ctx,
226 svn_client_ctx_t *ctx,
230 const char *mimetype = NULL;
231 svn_checksum_t *result_md5_checksum;
232 const char *text_checksum;
233 apr_hash_t* properties;
234 apr_hash_index_t *hi;
236 SVN_ERR(svn_path_check_valid(local_abspath, pool));
238 /* Add the file, using the pool from the FILES hash. */
239 SVN_ERR(editor->add_file(edit_path, dir_baton, NULL, SVN_INVALID_REVNUM,
242 /* Remember that the repository was modified */
243 import_ctx->repos_changed = TRUE;
245 if (! dirent->special)
247 /* add automatic properties */
248 SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype,
250 import_ctx->magic_cookie,
251 import_ctx->autoprops,
255 properties = apr_hash_make(pool);
259 for (hi = apr_hash_first(pool, properties); hi; hi = apr_hash_next(hi))
261 const char *pname = apr_hash_this_key(hi);
262 const svn_string_t *pval = apr_hash_this_val(hi);
264 SVN_ERR(editor->change_file_prop(file_baton, pname, pval, pool));
268 if (ctx->notify_func2)
270 svn_wc_notify_t *notify
271 = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added,
273 notify->kind = svn_node_file;
274 notify->mime_type = mimetype;
275 notify->content_state = notify->prop_state
276 = svn_wc_notify_state_inapplicable;
277 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
278 ctx->notify_func2(ctx->notify_baton2, notify, pool);
281 /* If this is a special file, we need to set the svn:special
282 property and create a temporary detranslated version in order to
283 send to the server. */
286 svn_hash_sets(properties, SVN_PROP_SPECIAL,
287 svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool));
288 SVN_ERR(editor->change_file_prop(file_baton, SVN_PROP_SPECIAL,
289 svn_hash_gets(properties,
294 /* Now, transmit the file contents. */
295 SVN_ERR(send_file_contents(&result_md5_checksum, local_abspath,
296 file_baton, editor, properties, pool));
298 /* Finally, close the file. */
299 text_checksum = svn_checksum_to_cstring(result_md5_checksum, pool);
300 return svn_error_trace(editor->close_file(file_baton, text_checksum, pool));
304 /* Return in CHILDREN a mapping of basenames to dirents for the importable
305 * children of DIR_ABSPATH. EXCLUDES is a hash of absolute paths to filter
306 * out. IGNORES and GLOBAL_IGNORES, if non-NULL, are lists of basename
307 * patterns to filter out.
308 * FILTER_CALLBACK and FILTER_BATON will be called for each absolute path,
309 * allowing users to further filter the list of returned entries.
311 * Results are returned in RESULT_POOL; use SCRATCH_POOL for temporary data.*/
313 get_filtered_children(apr_hash_t **children,
314 const char *dir_abspath,
315 apr_hash_t *excludes,
316 apr_array_header_t *ignores,
317 apr_array_header_t *global_ignores,
318 svn_client_import_filter_func_t filter_callback,
320 svn_client_ctx_t *ctx,
321 apr_pool_t *result_pool,
322 apr_pool_t *scratch_pool)
325 apr_hash_index_t *hi;
326 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
328 SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, result_pool,
331 for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
333 const char *base_name = apr_hash_this_key(hi);
334 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
335 const char *local_abspath;
337 svn_pool_clear(iterpool);
339 local_abspath = svn_dirent_join(dir_abspath, base_name, iterpool);
341 if (svn_wc_is_adm_dir(base_name, iterpool))
343 /* If someone's trying to import a directory named the same
344 as our administrative directories, that's probably not
345 what they wanted to do. If they are importing a file
346 with that name, something is bound to blow up when they
347 checkout what they've imported. So, just skip items with
349 if (ctx->notify_func2)
351 svn_wc_notify_t *notify
352 = svn_wc_create_notify(svn_dirent_join(local_abspath, base_name,
354 svn_wc_notify_skip, iterpool);
355 notify->kind = svn_node_dir;
356 notify->content_state = notify->prop_state
357 = svn_wc_notify_state_inapplicable;
358 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
359 ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
362 svn_hash_sets(dirents, base_name, NULL);
365 /* If this is an excluded path, exclude it. */
366 if (svn_hash_gets(excludes, local_abspath))
368 svn_hash_sets(dirents, base_name, NULL);
372 if (ignores && svn_wc_match_ignore_list(base_name, ignores, iterpool))
374 svn_hash_sets(dirents, base_name, NULL);
378 if (global_ignores &&
379 svn_wc_match_ignore_list(base_name, global_ignores, iterpool))
381 svn_hash_sets(dirents, base_name, NULL);
387 svn_boolean_t filter = FALSE;
389 SVN_ERR(filter_callback(filter_baton, &filter, local_abspath,
394 svn_hash_sets(dirents, base_name, NULL);
399 svn_pool_destroy(iterpool);
406 import_dir(const svn_delta_editor_t *editor,
408 const char *local_abspath,
409 const char *edit_path,
411 apr_hash_t *excludes,
412 apr_array_header_t *global_ignores,
413 svn_boolean_t no_ignore,
414 svn_boolean_t no_autoprops,
415 svn_boolean_t ignore_unknown_node_types,
416 svn_client_import_filter_func_t filter_callback,
418 import_ctx_t *import_ctx,
419 svn_client_ctx_t *ctx,
423 /* Import the children of DIR_ABSPATH, with other arguments similar to
426 import_children(const char *dir_abspath,
427 const char *edit_path,
429 const svn_delta_editor_t *editor,
432 apr_hash_t *excludes,
433 apr_array_header_t *global_ignores,
434 svn_boolean_t no_ignore,
435 svn_boolean_t no_autoprops,
436 svn_boolean_t ignore_unknown_node_types,
437 svn_client_import_filter_func_t filter_callback,
439 import_ctx_t *import_ctx,
440 svn_client_ctx_t *ctx,
441 apr_pool_t *scratch_pool)
443 apr_array_header_t *sorted_dirents;
445 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
447 sorted_dirents = svn_sort__hash(dirents, svn_sort_compare_items_lexically,
449 for (i = 0; i < sorted_dirents->nelts; i++)
451 const char *this_abspath, *this_edit_path;
452 svn_sort__item_t item = APR_ARRAY_IDX(sorted_dirents, i,
454 const char *filename = item.key;
455 const svn_io_dirent2_t *dirent = item.value;
457 svn_pool_clear(iterpool);
459 if (ctx->cancel_func)
460 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
462 /* Typically, we started importing from ".", in which case
463 edit_path is "". So below, this_path might become "./blah",
464 and this_edit_path might become "blah", for example. */
465 this_abspath = svn_dirent_join(dir_abspath, filename, iterpool);
466 this_edit_path = svn_relpath_join(edit_path, filename, iterpool);
468 if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates)
471 svn_depth_t depth_below_here = depth;
472 if (depth == svn_depth_immediates)
473 depth_below_here = svn_depth_empty;
475 SVN_ERR(import_dir(editor, dir_baton, this_abspath,
476 this_edit_path, depth_below_here, excludes,
477 global_ignores, no_ignore, no_autoprops,
478 ignore_unknown_node_types, filter_callback,
479 filter_baton, import_ctx, ctx, iterpool));
481 else if (dirent->kind == svn_node_file && depth >= svn_depth_files)
483 SVN_ERR(import_file(editor, dir_baton, this_abspath,
484 this_edit_path, dirent,
485 import_ctx, ctx, iterpool));
487 else if (dirent->kind != svn_node_dir && dirent->kind != svn_node_file)
489 if (ignore_unknown_node_types)
492 if (ctx->notify_func2)
494 svn_wc_notify_t *notify
495 = svn_wc_create_notify(this_abspath,
496 svn_wc_notify_skip, iterpool);
497 notify->kind = svn_node_dir;
498 notify->content_state = notify->prop_state
499 = svn_wc_notify_state_inapplicable;
500 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
501 ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
505 return svn_error_createf
506 (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
507 _("Unknown or unversionable type for '%s'"),
508 svn_dirent_local_style(this_abspath, iterpool));
512 svn_pool_destroy(iterpool);
517 /* Import directory LOCAL_ABSPATH into the repository directory indicated by
518 * DIR_BATON in EDITOR. EDIT_PATH is the path imported as the root
519 * directory, so all edits are relative to that.
521 * DEPTH is the depth at this point in the descent (it may be changed
522 * for recursive calls).
524 * Accumulate file paths and their batons in FILES, which must be
525 * non-null. (These are used to send postfix textdeltas later).
527 * EXCLUDES is a hash whose keys are absolute paths to exclude from
528 * the import (values are unused).
530 * GLOBAL_IGNORES is an array of const char * ignore patterns. Any child
531 * of LOCAL_ABSPATH which matches one or more of the patterns is not imported.
533 * If NO_IGNORE is FALSE, don't import files or directories that match
536 * If FILTER_CALLBACK is not NULL, call it with FILTER_BATON on each to be
537 * imported node below LOCAL_ABSPATH to allow filtering nodes.
539 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each
542 * Use POOL for any temporary allocation. */
544 import_dir(const svn_delta_editor_t *editor,
546 const char *local_abspath,
547 const char *edit_path,
549 apr_hash_t *excludes,
550 apr_array_header_t *global_ignores,
551 svn_boolean_t no_ignore,
552 svn_boolean_t no_autoprops,
553 svn_boolean_t ignore_unknown_node_types,
554 svn_client_import_filter_func_t filter_callback,
556 import_ctx_t *import_ctx,
557 svn_client_ctx_t *ctx,
561 void *this_dir_baton;
563 SVN_ERR(svn_path_check_valid(local_abspath, pool));
564 SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes, NULL,
565 global_ignores, filter_callback,
566 filter_baton, ctx, pool, pool));
568 /* Import this directory, but not yet its children. */
570 /* Add the new subdirectory, getting a descent baton from the editor. */
571 SVN_ERR(editor->add_directory(edit_path, dir_baton, NULL,
572 SVN_INVALID_REVNUM, pool, &this_dir_baton));
574 /* Remember that the repository was modified */
575 import_ctx->repos_changed = TRUE;
577 /* By notifying before the recursive call below, we display
578 a directory add before displaying adds underneath the
579 directory. To do it the other way around, just move this
580 after the recursive call. */
581 if (ctx->notify_func2)
583 svn_wc_notify_t *notify
584 = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added,
586 notify->kind = svn_node_dir;
587 notify->content_state = notify->prop_state
588 = svn_wc_notify_state_inapplicable;
589 notify->lock_state = svn_wc_notify_lock_state_inapplicable;
590 ctx->notify_func2(ctx->notify_baton2, notify, pool);
594 /* Now import the children recursively. */
595 SVN_ERR(import_children(local_abspath, edit_path, dirents, editor,
596 this_dir_baton, depth, excludes, global_ignores,
597 no_ignore, no_autoprops, ignore_unknown_node_types,
598 filter_callback, filter_baton,
599 import_ctx, ctx, pool));
601 /* Finally, close the sub-directory. */
602 SVN_ERR(editor->close_directory(this_dir_baton, pool));
608 /* Recursively import LOCAL_ABSPATH to a repository using EDITOR and
609 * EDIT_BATON. LOCAL_ABSPATH can be a file or directory.
611 * Sets *UPDATED_REPOSITORY to TRUE when the repository was modified by
612 * a successfull commit, otherwise to FALSE.
614 * DEPTH is the depth at which to import LOCAL_ABSPATH; it behaves as for
615 * svn_client_import5().
617 * BASE_REV is the revision to use for the root of the commit. We
618 * checked the preconditions against this revision.
620 * NEW_ENTRIES is an ordered array of path components that must be
621 * created in the repository (where the ordering direction is
622 * parent-to-child). If LOCAL_ABSPATH is a directory, NEW_ENTRIES may be empty
623 * -- the result is an import which creates as many new entries in the
624 * top repository target directory as there are importable entries in
625 * the top of LOCAL_ABSPATH; but if NEW_ENTRIES is not empty, its last item is
626 * the name of a new subdirectory in the repository to hold the
627 * import. If LOCAL_ABSPATH is a file, NEW_ENTRIES may not be empty, and its
628 * last item is the name used for the file in the repository. If
629 * NEW_ENTRIES contains more than one item, all but the last item are
630 * the names of intermediate directories that are created before the
631 * real import begins. NEW_ENTRIES may NOT be NULL.
633 * EXCLUDES is a hash whose keys are absolute paths to exclude from
634 * the import (values are unused).
636 * AUTOPROPS is hash of all config file autoprops and
637 * svn:auto-props inherited by the import target, see the
638 * IMPORT_CTX member of the same name.
640 * LOCAL_IGNORES is an array of const char * ignore patterns which
641 * correspond to the svn:ignore property (if any) set on the root of the
642 * repository target and thus dictates which immediate children of that
643 * target should be ignored and not imported.
645 * GLOBAL_IGNORES is an array of const char * ignore patterns which
646 * correspond to the svn:global-ignores properties (if any) set on
647 * the root of the repository target or inherited by it.
649 * If NO_IGNORE is FALSE, don't import files or directories that match
652 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for
653 * each imported path, passing actions svn_wc_notify_commit_added.
655 * URL is used only in the 'commit_finalizing' notification.
657 * Use POOL for any temporary allocation.
659 * Note: the repository directory receiving the import was specified
660 * when the editor was fetched. (I.e, when EDITOR->open_root() is
661 * called, it returns a directory baton for that directory, which is
662 * not necessarily the root.)
665 import(svn_boolean_t *updated_repository,
666 const char *local_abspath,
668 const apr_array_header_t *new_entries,
669 const svn_delta_editor_t *editor,
672 svn_revnum_t base_rev,
673 apr_hash_t *excludes,
674 apr_hash_t *autoprops,
675 apr_array_header_t *local_ignores,
676 apr_array_header_t *global_ignores,
677 svn_boolean_t no_ignore,
678 svn_boolean_t no_autoprops,
679 svn_boolean_t ignore_unknown_node_types,
680 svn_client_import_filter_func_t filter_callback,
682 svn_client_ctx_t *ctx,
686 apr_array_header_t *batons = NULL;
687 const char *edit_path = "";
688 import_ctx_t import_ctx = { FALSE };
689 const svn_io_dirent2_t *dirent;
691 *updated_repository = FALSE;
693 import_ctx.autoprops = autoprops;
694 SVN_ERR(svn_magic__init(&import_ctx.magic_cookie, ctx->config, pool));
696 /* Get a root dir baton. We pass the revnum we used for testing our
697 assumptions and obtaining inherited properties. */
698 SVN_ERR(editor->open_root(edit_baton, base_rev, pool, &root_baton));
700 /* Import a file or a directory tree. */
701 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, FALSE,
704 /* Make the intermediate directory components necessary for properly
705 rooting our import source tree. */
706 if (new_entries->nelts)
710 batons = apr_array_make(pool, new_entries->nelts, sizeof(void *));
711 for (i = 0; i < new_entries->nelts; i++)
713 const char *component = APR_ARRAY_IDX(new_entries, i, const char *);
714 edit_path = svn_relpath_join(edit_path, component, pool);
716 /* If this is the last path component, and we're importing a
717 file, then this component is the name of the file, not an
718 intermediate directory. */
719 if ((i == new_entries->nelts - 1) && (dirent->kind == svn_node_file))
722 APR_ARRAY_PUSH(batons, void *) = root_baton;
723 SVN_ERR(editor->add_directory(edit_path,
725 NULL, SVN_INVALID_REVNUM,
728 /* Remember that the repository was modified */
729 import_ctx.repos_changed = TRUE;
732 else if (dirent->kind == svn_node_file)
734 return svn_error_create
735 (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
736 _("New entry name required when importing a file"));
739 /* Note that there is no need to check whether PATH's basename is
740 the same name that we reserve for our administrative
741 subdirectories. It would be strange -- though not illegal -- to
742 import the contents of a directory of that name, because the
743 directory's own name is not part of those contents. Of course,
744 if something underneath it also has our reserved name, then we'll
747 if (dirent->kind == svn_node_file)
749 /* This code path ignores EXCLUDES and FILTER, but they don't make
750 much sense for a single file import anyway. */
751 svn_boolean_t ignores_match = FALSE;
755 (svn_wc_match_ignore_list(local_abspath, global_ignores, pool)
756 || svn_wc_match_ignore_list(local_abspath, local_ignores, pool));
759 SVN_ERR(import_file(editor, root_baton, local_abspath, edit_path,
760 dirent, &import_ctx, ctx, pool));
762 else if (dirent->kind == svn_node_dir)
766 /* If we are creating a new repository directory path to import to,
767 then we disregard any svn:ignore property. */
768 if (!no_ignore && new_entries->nelts)
769 local_ignores = NULL;
771 SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes,
772 local_ignores, global_ignores,
773 filter_callback, filter_baton, ctx,
776 SVN_ERR(import_children(local_abspath, edit_path, dirents, editor,
777 root_baton, depth, excludes, global_ignores,
778 no_ignore, no_autoprops,
779 ignore_unknown_node_types, filter_callback,
780 filter_baton, &import_ctx, ctx, pool));
783 else if (dirent->kind == svn_node_none
784 || dirent->kind == svn_node_unknown)
786 return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
787 _("'%s' does not exist"),
788 svn_dirent_local_style(local_abspath, pool));
791 /* Close up shop; it's time to go home. */
792 SVN_ERR(editor->close_directory(root_baton, pool));
793 if (batons && batons->nelts)
796 while ((baton = (void **) apr_array_pop(batons)))
798 SVN_ERR(editor->close_directory(*baton, pool));
802 if (import_ctx.repos_changed)
804 if (ctx->notify_func2)
806 svn_wc_notify_t *notify;
807 notify = svn_wc_create_notify_url(url,
808 svn_wc_notify_commit_finalizing,
810 ctx->notify_func2(ctx->notify_baton2, notify, pool);
813 SVN_ERR(editor->close_edit(edit_baton, pool));
815 *updated_repository = TRUE;
822 /*** Public Interfaces. ***/
825 svn_client_import5(const char *path,
828 svn_boolean_t no_ignore,
829 svn_boolean_t no_autoprops,
830 svn_boolean_t ignore_unknown_node_types,
831 const apr_hash_t *revprop_table,
832 svn_client_import_filter_func_t filter_callback,
834 svn_commit_callback2_t commit_callback,
836 svn_client_ctx_t *ctx,
837 apr_pool_t *scratch_pool)
839 svn_error_t *err = SVN_NO_ERROR;
840 const char *log_msg = "";
841 const svn_delta_editor_t *editor;
843 svn_ra_session_t *ra_session;
844 apr_hash_t *excludes = apr_hash_make(scratch_pool);
845 svn_node_kind_t kind;
846 const char *local_abspath;
847 apr_array_header_t *new_entries = apr_array_make(scratch_pool, 4,
848 sizeof(const char *));
849 apr_hash_t *commit_revprops;
850 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
851 apr_hash_t *autoprops = NULL;
852 apr_array_header_t *global_ignores;
853 apr_array_header_t *local_ignores_arr;
854 svn_revnum_t base_rev;
855 apr_array_header_t *inherited_props = NULL;
856 apr_hash_t *url_props = NULL;
857 svn_boolean_t updated_repository;
859 if (svn_path_is_url(path))
860 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
861 _("'%s' is not a local path"), path);
863 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
865 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
867 /* Create a new commit item and add it to the array. */
868 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
870 /* If there's a log message gatherer, create a temporary commit
871 item array solely to help generate the log message. The
872 array is not used for the import itself. */
873 svn_client_commit_item3_t *item;
874 const char *tmp_file;
875 apr_array_header_t *commit_items
876 = apr_array_make(scratch_pool, 1, sizeof(item));
878 item = svn_client_commit_item3_create(scratch_pool);
879 item->path = local_abspath;
882 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
883 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
885 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
891 const char *abs_path;
892 SVN_ERR(svn_dirent_get_absolute(&abs_path, tmp_file, scratch_pool));
893 svn_hash_sets(excludes, abs_path, (void *)1);
897 SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
898 ctx, scratch_pool, iterpool));
900 SVN_ERR(svn_ra_get_latest_revnum(ra_session, &base_rev, iterpool));
902 /* Figure out all the path components we need to create just to have
903 a place to stick our imported tree. */
904 SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool));
906 /* We can import into directories, but if a file already exists, that's
908 if (kind == svn_node_file)
909 return svn_error_createf
910 (SVN_ERR_ENTRY_EXISTS, NULL,
911 _("Path '%s' already exists"), url);
913 while (kind == svn_node_none)
917 svn_pool_clear(iterpool);
919 svn_uri_split(&url, &dir, url, scratch_pool);
920 APR_ARRAY_PUSH(new_entries, const char *) = dir;
921 SVN_ERR(svn_ra_reparent(ra_session, url, iterpool));
923 SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool));
926 /* Reverse the order of the components we added to our NEW_ENTRIES array. */
927 svn_sort__array_reverse(new_entries, scratch_pool);
929 /* The repository doesn't know about the reserved administrative
931 if (new_entries->nelts)
933 const char *last_component
934 = APR_ARRAY_IDX(new_entries, new_entries->nelts - 1, const char *);
936 if (svn_wc_is_adm_dir(last_component, scratch_pool))
937 return svn_error_createf
938 (SVN_ERR_CL_ADM_DIR_RESERVED, NULL,
939 _("'%s' is a reserved name and cannot be imported"),
940 svn_dirent_local_style(last_component, scratch_pool));
943 SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
944 log_msg, ctx, scratch_pool));
946 /* Obtain properties before opening the commit editor, as at that point we are
947 not allowed to use the existing ra-session */
948 if (! no_ignore /*|| ! no_autoprops*/)
950 SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &url_props, "",
951 base_rev, SVN_DIRENT_KIND, scratch_pool));
953 SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", base_rev,
954 scratch_pool, iterpool));
957 /* Fetch RA commit editor. */
958 SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
959 svn_client__get_shim_callbacks(ctx->wc_ctx,
960 NULL, scratch_pool)));
961 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
962 commit_revprops, commit_callback,
963 commit_baton, NULL, TRUE,
966 /* Get inherited svn:auto-props, svn:global-ignores, and
967 svn:ignores for the location we are importing to. */
970 /* ### This should use inherited_props and url_props to avoid creating
971 another ra session to obtain the same values, but using a possibly
972 different HEAD revision */
973 SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx,
974 scratch_pool, iterpool));
978 global_ignores = NULL;
979 local_ignores_arr = NULL;
983 apr_array_header_t *config_ignores;
987 global_ignores = apr_array_make(scratch_pool, 64, sizeof(const char *));
989 SVN_ERR(svn_wc_get_default_ignores(&config_ignores, ctx->config,
991 global_ignores = apr_array_append(scratch_pool, global_ignores,
994 val = svn_hash_gets(url_props, SVN_PROP_INHERITABLE_IGNORES);
996 svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ",
997 FALSE, scratch_pool);
999 for (i = 0; i < inherited_props->nelts; i++)
1001 svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
1002 inherited_props, i, svn_prop_inherited_item_t *);
1004 val = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES);
1007 svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ",
1008 FALSE, scratch_pool);
1010 local_ignores_arr = apr_array_make(scratch_pool, 1, sizeof(const char *));
1012 val = svn_hash_gets(url_props, SVN_PROP_IGNORE);
1016 svn_cstring_split_append(local_ignores_arr, val->data,
1017 "\n\r\t\v ", FALSE, scratch_pool);
1021 /* If an error occurred during the commit, properly abort the edit. */
1022 err = svn_error_trace(import(&updated_repository,
1023 local_abspath, url, new_entries, editor,
1024 edit_baton, depth, base_rev, excludes,
1025 autoprops, local_ignores_arr, global_ignores,
1026 no_ignore, no_autoprops,
1027 ignore_unknown_node_types, filter_callback,
1028 filter_baton, ctx, iterpool));
1030 svn_pool_destroy(iterpool);
1032 if (err || !updated_repository)
1034 return svn_error_compose_create(
1036 editor->abort_edit(edit_baton, scratch_pool));
1039 return SVN_NO_ERROR;