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);