1/* 2 * import.c: wrappers around import functionality. 3 * 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 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 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 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* ==================================================================== */ 25 26 27 28/*** Includes. ***/ 29 30#include <string.h> 31#include <apr_strings.h> 32#include <apr_hash.h> 33#include <apr_md5.h> 34 35#include "svn_hash.h" 36#include "svn_ra.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" 44#include "svn_path.h" 45#include "svn_io.h" 46#include "svn_sorts.h" 47#include "svn_props.h" 48 49#include "client.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" 54 55#include "svn_private_config.h" 56 57/* Import context baton. */ 58 59typedef struct import_ctx_t 60{ 61 /* Whether any changes were made to the repository */ 62 svn_boolean_t repos_changed; 63 64 /* A magic cookie for mime-type detection. */ 65 svn_magic__cookie_t *magic_cookie; 66 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; 74} import_ctx_t; 75 76typedef struct open_txdelta_stream_baton_t 77{ 78 svn_boolean_t need_reset; 79 svn_stream_t *stream; 80} open_txdelta_stream_baton_t; 81 82/* Implements svn_txdelta_stream_open_func_t */ 83static svn_error_t * 84open_txdelta_stream(svn_txdelta_stream_t **txdelta_stream_p, 85 void *baton, 86 apr_pool_t *result_pool, 87 apr_pool_t *scratch_pool) 88{ 89 open_txdelta_stream_baton_t *b = baton; 90 91 if (b->need_reset) 92 { 93 /* Under rare circumstances, we can be restarted and would need to 94 * supply the delta stream again. In this case, reset the base 95 * stream. */ 96 SVN_ERR(svn_stream_reset(b->stream)); 97 } 98 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; 103 return SVN_NO_ERROR; 104} 105 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. 109 110 Return the resulting checksum in *RESULT_MD5_CHECKSUM_P. */ 111 112/* ### how does this compare against svn_wc_transmit_text_deltas2() ??? */ 113 114static svn_error_t * 115send_file_contents(svn_checksum_t **result_md5_checksum_p, 116 const char *local_abspath, 117 void *file_baton, 118 const svn_delta_editor_t *editor, 119 apr_hash_t *properties, 120 apr_pool_t *pool) 121{ 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; 126 const char *eol; 127 apr_hash_t *keywords; 128 open_txdelta_stream_baton_t baton = { 0 }; 129 130 /* If there are properties, look for EOL-style and keywords ones. */ 131 if (properties) 132 { 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)) 138 special = TRUE; 139 } 140 141 if (eol_style_val) 142 svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data); 143 else 144 { 145 eol = NULL; 146 eol_style = svn_subst_eol_style_none; 147 } 148 149 if (keywords_val) 150 SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data, 151 APR_STRINGIFY(SVN_INVALID_REVNUM), 152 "", "", 0, "", pool)); 153 else 154 keywords = NULL; 155 156 if (special) 157 { 158 SVN_ERR(svn_subst_read_specialfile(&contents, local_abspath, 159 pool, pool)); 160 } 161 else 162 { 163 /* Open the working copy file. */ 164 SVN_ERR(svn_stream_open_readonly(&contents, local_abspath, pool, pool)); 165 166 /* If we have EOL styles or keywords, then detranslate the file. */ 167 if (svn_subst_translation_required(eol_style, eol, keywords, 168 FALSE, TRUE)) 169 { 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'"), 174 SVN_PROP_EOL_STYLE, 175 svn_dirent_local_style(local_abspath, 176 pool), 177 eol_style_val->data); 178 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; 183 184 /* Wrap the working copy stream with a filter to detranslate it. */ 185 contents = svn_subst_stream_translated(contents, 186 eol, 187 TRUE /* repair */, 188 keywords, 189 FALSE /* expand */, 190 pool); 191 } 192 } 193 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)); 203 204 return SVN_NO_ERROR; 205} 206 207 208/* Import file PATH as EDIT_PATH in the repository directory indicated 209 * by DIR_BATON in EDITOR. 210 * 211 * Accumulate file paths and their batons in FILES, which must be 212 * non-null. (These are used to send postfix textdeltas later). 213 * 214 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON 215 * for each file. 216 * 217 * Use POOL for any temporary allocation. 218 */ 219static svn_error_t * 220import_file(const svn_delta_editor_t *editor, 221 void *dir_baton, 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, 227 apr_pool_t *pool) 228{ 229 void *file_baton; 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; 235 236 SVN_ERR(svn_path_check_valid(local_abspath, pool)); 237 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, 240 pool, &file_baton)); 241 242 /* Remember that the repository was modified */ 243 import_ctx->repos_changed = TRUE; 244 245 if (! dirent->special) 246 { 247 /* add automatic properties */ 248 SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype, 249 local_abspath, 250 import_ctx->magic_cookie, 251 import_ctx->autoprops, 252 ctx, pool, pool)); 253 } 254 else 255 properties = apr_hash_make(pool); 256 257 if (properties) 258 { 259 for (hi = apr_hash_first(pool, properties); hi; hi = apr_hash_next(hi)) 260 { 261 const char *pname = apr_hash_this_key(hi); 262 const svn_string_t *pval = apr_hash_this_val(hi); 263 264 SVN_ERR(editor->change_file_prop(file_baton, pname, pval, pool)); 265 } 266 } 267 268 if (ctx->notify_func2) 269 { 270 svn_wc_notify_t *notify 271 = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added, 272 pool); 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); 279 } 280 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. */ 284 if (dirent->special) 285 { 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, 290 SVN_PROP_SPECIAL), 291 pool)); 292 } 293 294 /* Now, transmit the file contents. */ 295 SVN_ERR(send_file_contents(&result_md5_checksum, local_abspath, 296 file_baton, editor, properties, pool)); 297 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)); 301} 302 303 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. 310 * 311 * Results are returned in RESULT_POOL; use SCRATCH_POOL for temporary data.*/ 312static svn_error_t * 313get_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, 319 void *filter_baton, 320 svn_client_ctx_t *ctx, 321 apr_pool_t *result_pool, 322 apr_pool_t *scratch_pool) 323{ 324 apr_hash_t *dirents; 325 apr_hash_index_t *hi; 326 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 327 328 SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, result_pool, 329 scratch_pool)); 330 331 for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) 332 { 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; 336 337 svn_pool_clear(iterpool); 338 339 local_abspath = svn_dirent_join(dir_abspath, base_name, iterpool); 340 341 if (svn_wc_is_adm_dir(base_name, iterpool)) 342 { 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 348 that name. */ 349 if (ctx->notify_func2) 350 { 351 svn_wc_notify_t *notify 352 = svn_wc_create_notify(svn_dirent_join(local_abspath, base_name, 353 iterpool), 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); 360 } 361 362 svn_hash_sets(dirents, base_name, NULL); 363 continue; 364 } 365 /* If this is an excluded path, exclude it. */ 366 if (svn_hash_gets(excludes, local_abspath)) 367 { 368 svn_hash_sets(dirents, base_name, NULL); 369 continue; 370 } 371 372 if (ignores && svn_wc_match_ignore_list(base_name, ignores, iterpool)) 373 { 374 svn_hash_sets(dirents, base_name, NULL); 375 continue; 376 } 377 378 if (global_ignores && 379 svn_wc_match_ignore_list(base_name, global_ignores, iterpool)) 380 { 381 svn_hash_sets(dirents, base_name, NULL); 382 continue; 383 } 384 385 if (filter_callback) 386 { 387 svn_boolean_t filter = FALSE; 388 389 SVN_ERR(filter_callback(filter_baton, &filter, local_abspath, 390 dirent, iterpool)); 391 392 if (filter) 393 { 394 svn_hash_sets(dirents, base_name, NULL); 395 continue; 396 } 397 } 398 } 399 svn_pool_destroy(iterpool); 400 401 *children = dirents; 402 return SVN_NO_ERROR; 403} 404 405static svn_error_t * 406import_dir(const svn_delta_editor_t *editor, 407 void *dir_baton, 408 const char *local_abspath, 409 const char *edit_path, 410 svn_depth_t depth, 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, 417 void *filter_baton, 418 import_ctx_t *import_ctx, 419 svn_client_ctx_t *ctx, 420 apr_pool_t *pool); 421 422 423/* Import the children of DIR_ABSPATH, with other arguments similar to 424 * import_dir(). */ 425static svn_error_t * 426import_children(const char *dir_abspath, 427 const char *edit_path, 428 apr_hash_t *dirents, 429 const svn_delta_editor_t *editor, 430 void *dir_baton, 431 svn_depth_t depth, 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, 438 void *filter_baton, 439 import_ctx_t *import_ctx, 440 svn_client_ctx_t *ctx, 441 apr_pool_t *scratch_pool) 442{ 443 apr_array_header_t *sorted_dirents; 444 int i; 445 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 446 447 sorted_dirents = svn_sort__hash(dirents, svn_sort_compare_items_lexically, 448 scratch_pool); 449 for (i = 0; i < sorted_dirents->nelts; i++) 450 { 451 const char *this_abspath, *this_edit_path; 452 svn_sort__item_t item = APR_ARRAY_IDX(sorted_dirents, i, 453 svn_sort__item_t); 454 const char *filename = item.key; 455 const svn_io_dirent2_t *dirent = item.value; 456 457 svn_pool_clear(iterpool); 458 459 if (ctx->cancel_func) 460 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 461 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); 467 468 if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates) 469 { 470 /* Recurse. */ 471 svn_depth_t depth_below_here = depth; 472 if (depth == svn_depth_immediates) 473 depth_below_here = svn_depth_empty; 474 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)); 480 } 481 else if (dirent->kind == svn_node_file && depth >= svn_depth_files) 482 { 483 SVN_ERR(import_file(editor, dir_baton, this_abspath, 484 this_edit_path, dirent, 485 import_ctx, ctx, iterpool)); 486 } 487 else if (dirent->kind != svn_node_dir && dirent->kind != svn_node_file) 488 { 489 if (ignore_unknown_node_types) 490 { 491 /*## warn about it*/ 492 if (ctx->notify_func2) 493 { 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); 502 } 503 } 504 else 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)); 509 } 510 } 511 512 svn_pool_destroy(iterpool); 513 return SVN_NO_ERROR; 514} 515 516 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. 520 * 521 * DEPTH is the depth at this point in the descent (it may be changed 522 * for recursive calls). 523 * 524 * Accumulate file paths and their batons in FILES, which must be 525 * non-null. (These are used to send postfix textdeltas later). 526 * 527 * EXCLUDES is a hash whose keys are absolute paths to exclude from 528 * the import (values are unused). 529 * 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. 532 * 533 * If NO_IGNORE is FALSE, don't import files or directories that match 534 * ignore patterns. 535 * 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. 538 * 539 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each 540 * directory. 541 * 542 * Use POOL for any temporary allocation. */ 543static svn_error_t * 544import_dir(const svn_delta_editor_t *editor, 545 void *dir_baton, 546 const char *local_abspath, 547 const char *edit_path, 548 svn_depth_t depth, 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, 555 void *filter_baton, 556 import_ctx_t *import_ctx, 557 svn_client_ctx_t *ctx, 558 apr_pool_t *pool) 559{ 560 apr_hash_t *dirents; 561 void *this_dir_baton; 562 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)); 567 568 /* Import this directory, but not yet its children. */ 569 { 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)); 573 574 /* Remember that the repository was modified */ 575 import_ctx->repos_changed = TRUE; 576 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) 582 { 583 svn_wc_notify_t *notify 584 = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added, 585 pool); 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); 591 } 592 } 593 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)); 600 601 /* Finally, close the sub-directory. */ 602 SVN_ERR(editor->close_directory(this_dir_baton, pool)); 603 604 return SVN_NO_ERROR; 605} 606 607 608/* Recursively import LOCAL_ABSPATH to a repository using EDITOR and 609 * EDIT_BATON. LOCAL_ABSPATH can be a file or directory. 610 * 611 * Sets *UPDATED_REPOSITORY to TRUE when the repository was modified by 612 * a successfull commit, otherwise to FALSE. 613 * 614 * DEPTH is the depth at which to import LOCAL_ABSPATH; it behaves as for 615 * svn_client_import5(). 616 * 617 * BASE_REV is the revision to use for the root of the commit. We 618 * checked the preconditions against this revision. 619 * 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. 632 * 633 * EXCLUDES is a hash whose keys are absolute paths to exclude from 634 * the import (values are unused). 635 * 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. 639 * 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. 644 * 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. 648 * 649 * If NO_IGNORE is FALSE, don't import files or directories that match 650 * ignore patterns. 651 * 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. 654 * 655 * URL is used only in the 'commit_finalizing' notification. 656 * 657 * Use POOL for any temporary allocation. 658 * 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.) 663 */ 664static svn_error_t * 665import(svn_boolean_t *updated_repository, 666 const char *local_abspath, 667 const char *url, 668 const apr_array_header_t *new_entries, 669 const svn_delta_editor_t *editor, 670 void *edit_baton, 671 svn_depth_t depth, 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, 681 void *filter_baton, 682 svn_client_ctx_t *ctx, 683 apr_pool_t *pool) 684{ 685 void *root_baton; 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; 690 691 *updated_repository = FALSE; 692 693 import_ctx.autoprops = autoprops; 694 SVN_ERR(svn_magic__init(&import_ctx.magic_cookie, ctx->config, pool)); 695 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)); 699 700 /* Import a file or a directory tree. */ 701 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, FALSE, 702 pool, pool)); 703 704 /* Make the intermediate directory components necessary for properly 705 rooting our import source tree. */ 706 if (new_entries->nelts) 707 { 708 int i; 709 710 batons = apr_array_make(pool, new_entries->nelts, sizeof(void *)); 711 for (i = 0; i < new_entries->nelts; i++) 712 { 713 const char *component = APR_ARRAY_IDX(new_entries, i, const char *); 714 edit_path = svn_relpath_join(edit_path, component, pool); 715 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)) 720 break; 721 722 APR_ARRAY_PUSH(batons, void *) = root_baton; 723 SVN_ERR(editor->add_directory(edit_path, 724 root_baton, 725 NULL, SVN_INVALID_REVNUM, 726 pool, &root_baton)); 727 728 /* Remember that the repository was modified */ 729 import_ctx.repos_changed = TRUE; 730 } 731 } 732 else if (dirent->kind == svn_node_file) 733 { 734 return svn_error_create 735 (SVN_ERR_NODE_UNKNOWN_KIND, NULL, 736 _("New entry name required when importing a file")); 737 } 738 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 745 error. */ 746 747 if (dirent->kind == svn_node_file) 748 { 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; 752 753 if (!no_ignore) 754 ignores_match = 755 (svn_wc_match_ignore_list(local_abspath, global_ignores, pool) 756 || svn_wc_match_ignore_list(local_abspath, local_ignores, pool)); 757 758 if (!ignores_match) 759 SVN_ERR(import_file(editor, root_baton, local_abspath, edit_path, 760 dirent, &import_ctx, ctx, pool)); 761 } 762 else if (dirent->kind == svn_node_dir) 763 { 764 apr_hash_t *dirents; 765 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; 770 771 SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes, 772 local_ignores, global_ignores, 773 filter_callback, filter_baton, ctx, 774 pool, pool)); 775 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)); 781 782 } 783 else if (dirent->kind == svn_node_none 784 || dirent->kind == svn_node_unknown) 785 { 786 return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL, 787 _("'%s' does not exist"), 788 svn_dirent_local_style(local_abspath, pool)); 789 } 790 791 /* Close up shop; it's time to go home. */ 792 SVN_ERR(editor->close_directory(root_baton, pool)); 793 if (batons && batons->nelts) 794 { 795 void **baton; 796 while ((baton = (void **) apr_array_pop(batons))) 797 { 798 SVN_ERR(editor->close_directory(*baton, pool)); 799 } 800 } 801 802 if (import_ctx.repos_changed) 803 { 804 if (ctx->notify_func2) 805 { 806 svn_wc_notify_t *notify; 807 notify = svn_wc_create_notify_url(url, 808 svn_wc_notify_commit_finalizing, 809 pool); 810 ctx->notify_func2(ctx->notify_baton2, notify, pool); 811 } 812 813 SVN_ERR(editor->close_edit(edit_baton, pool)); 814 815 *updated_repository = TRUE; 816 } 817 818 return SVN_NO_ERROR; 819} 820 821 822/*** Public Interfaces. ***/ 823 824svn_error_t * 825svn_client_import5(const char *path, 826 const char *url, 827 svn_depth_t depth, 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, 833 void *filter_baton, 834 svn_commit_callback2_t commit_callback, 835 void *commit_baton, 836 svn_client_ctx_t *ctx, 837 apr_pool_t *scratch_pool) 838{ 839 svn_error_t *err = SVN_NO_ERROR; 840 const char *log_msg = ""; 841 const svn_delta_editor_t *editor; 842 void *edit_baton; 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; 858 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); 862 863 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); 864 865 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); 866 867 /* Create a new commit item and add it to the array. */ 868 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) 869 { 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)); 877 878 item = svn_client_commit_item3_create(scratch_pool); 879 item->path = local_abspath; 880 item->url = url; 881 item->kind = kind; 882 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; 883 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; 884 885 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items, 886 ctx, scratch_pool)); 887 if (! log_msg) 888 return SVN_NO_ERROR; 889 if (tmp_file) 890 { 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); 894 } 895 } 896 897 SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL, 898 ctx, scratch_pool, iterpool)); 899 900 SVN_ERR(svn_ra_get_latest_revnum(ra_session, &base_rev, iterpool)); 901 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)); 905 906 /* We can import into directories, but if a file already exists, that's 907 an error. */ 908 if (kind == svn_node_file) 909 return svn_error_createf 910 (SVN_ERR_ENTRY_EXISTS, NULL, 911 _("Path '%s' already exists"), url); 912 913 while (kind == svn_node_none) 914 { 915 const char *dir; 916 917 svn_pool_clear(iterpool); 918 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)); 922 923 SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool)); 924 } 925 926 /* Reverse the order of the components we added to our NEW_ENTRIES array. */ 927 svn_sort__array_reverse(new_entries, scratch_pool); 928 929 /* The repository doesn't know about the reserved administrative 930 directory. */ 931 if (new_entries->nelts) 932 { 933 const char *last_component 934 = APR_ARRAY_IDX(new_entries, new_entries->nelts - 1, const char *); 935 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)); 941 } 942 943 SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, 944 log_msg, ctx, scratch_pool)); 945 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*/) 949 { 950 SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &url_props, "", 951 base_rev, SVN_DIRENT_KIND, scratch_pool)); 952 953 SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", base_rev, 954 scratch_pool, iterpool)); 955 } 956 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, 964 scratch_pool)); 965 966 /* Get inherited svn:auto-props, svn:global-ignores, and 967 svn:ignores for the location we are importing to. */ 968 if (!no_autoprops) 969 { 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)); 975 } 976 if (no_ignore) 977 { 978 global_ignores = NULL; 979 local_ignores_arr = NULL; 980 } 981 else 982 { 983 apr_array_header_t *config_ignores; 984 svn_string_t *val; 985 int i; 986 987 global_ignores = apr_array_make(scratch_pool, 64, sizeof(const char *)); 988 989 SVN_ERR(svn_wc_get_default_ignores(&config_ignores, ctx->config, 990 scratch_pool)); 991 global_ignores = apr_array_append(scratch_pool, global_ignores, 992 config_ignores); 993 994 val = svn_hash_gets(url_props, SVN_PROP_INHERITABLE_IGNORES); 995 if (val) 996 svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ", 997 FALSE, scratch_pool); 998 999 for (i = 0; i < inherited_props->nelts; i++) 1000 { 1001 svn_prop_inherited_item_t *elt = APR_ARRAY_IDX( 1002 inherited_props, i, svn_prop_inherited_item_t *); 1003 1004 val = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES); 1005 1006 if (val) 1007 svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ", 1008 FALSE, scratch_pool); 1009 } 1010 local_ignores_arr = apr_array_make(scratch_pool, 1, sizeof(const char *)); 1011 1012 val = svn_hash_gets(url_props, SVN_PROP_IGNORE); 1013 1014 if (val) 1015 { 1016 svn_cstring_split_append(local_ignores_arr, val->data, 1017 "\n\r\t\v ", FALSE, scratch_pool); 1018 } 1019 } 1020 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)); 1029 1030 svn_pool_destroy(iterpool); 1031 1032 if (err || !updated_repository) 1033 { 1034 return svn_error_compose_create( 1035 err, 1036 editor->abort_edit(edit_baton, scratch_pool)); 1037 } 1038 1039 return SVN_NO_ERROR; 1040} 1041 1042